[모던C++입문] 3.1 함수 템플릿

짜장범벅·2022년 6월 30일
0

모던CPP입문

목록 보기
8/11

3.1 함수 템플릿

함수 템플릿은 잠재적으로 무한한 수의 함수 오버로드를 생성하는 청사진이다.

template<typename T>
T max(T a, T b){
    if (a > b)
        return a;
    else
        return b;
}

이 함수 템플릿은 오버로드를 치환하며, 함수 이름은 그대로 max다.

3.1.1 인스턴스화

컴파일러는 제네릭이 아닌 함수의 경우 정의를 읽고 오류를 검사한 뒤 실행 가능한 코드를 생상한다. 제네릭 함수의 경우에는 구문 분석 오류처럼 템플릿 매개변수기 독립적인 오류만 컴출할 수 있다.

template<typename T>
T max(T a, T b){
    if a > b //오류!
        return a;
    else
        return b;
}

위 코드의 경우 if문 뒤에 괄호가 없어 컴파일되지 않는다.

template<typename T>
inline T max(T x, T y) {
    return x < y ? y.value : x.value;
}

위 코드는 컴파일된다. 이 함수는 Tintdouble같은 타입으로는 호출할 수 없다.

함수 템플릿 자체만 컴파일하더라도 바이너리에서 코드를 생성하지 않는다. 우리가 함수 템플릿을 호출할 때만 코드를 생성한다. 이 경우, 우리는 이 함수 템플릿을 인스턴스화 한다. 그런 다음 컴파일러는 제네릭 함수가 주어진 인수 타입에 대해 올바른지 여부를 철저히 검사한다.

여기서 좀 더 명시적으로 템플릿 매개변수를 지환하는 타입을 선언하는 방법도 있다.

std::cout << max<float>(8.1, 9.3) << '\n';

이 방법을 사용하면 함수 호출 없이 인스턴스화를 강제할 수 있다. 이는 오브젝트 파일을 생성할 때 유용할 수 있다.

3.1.2 매개변수 타입 추론

3.1.2.1 값 매개변수

함수 템플릿의 매개변수도 const와 레퍼런스로 한정될 수 있다.

template<typename T>
T max(const T& a, const T& b) {
    ...
}

3.1.2.2 Lvalue 레퍼런스 매개변수

아래와 같이 모든 인수를 받을 수 있도록 상수 레퍼런스를 매개변수로 사용할 수 있다.

template<typename TPara>
void f2(const TPara& p) {
    ...
}

p는 한정되지 않은 인수 타입의 상수 레퍼런스이므로 p를 수정할 수 없다.

template<typename TPara>
void f3(TPara& p) {
    ...
}

int i=0;
int& j=i;
const int& k=i;

위 경우 참조할 수 없기 때문에 모든 리터럴과 임시 변수를 거부한다.

i와 같이 일반 int 변수를 전달할 때 TParaint로 치환되므로 p의 타입은 int&i를 참조한다.

j와 같이 변경 가능한 레퍼런스 변수를 전달할 때 동일한 치환 과정을 거친다.

k와 같이 const int 또는 const int&를 전달하면 TParaconst int로 지환되는 것은 가능하다. 따라서 pconst int가 된다.

3.1.2.3 포워드 레퍼런스

template<typename TPara>
void f4(TPara&& p) {
    ...
}

포워드 레퍼런스가 Rvalue, Lvalue 모두 받아들일 수 있다. 예를들면,

f4(3);
f4(move(i));
f4(move(up));

위 코드에서 TParaintunique_ptr<int>로 치환되고 p는 해당 인수 타입의 Rvalue 레퍼런스다.

ij같은 Lvalue를 사용해 f4를 호출하면 컴파일러는 이러한 인수를 템플릿 Rvalue 레퍼런스 매개변수로 사용한다.

3.1.2.4 퍼펙트 포워딩

우리는 move를 통해 Lvalue를 Rvalue로 변환할 수 있음을 알고있다. forward를 이용하면 Rvalue 레퍼런스를 Rvalue로, Lvalue 레퍼런스를 Lvalue로 변환한다.

3.1.3 템플릿의 오류 처리

비교 연산자가 정의되지 않은 인수에 대한 max를 수행하면 컴파일 오류가 발생한다. 템플릿 함수가 또 다른 템플릿 함수를 연속해서 호출하기 때문이다. 템플릿 함수를 컴파일할때는 구문을 분석하고 인스턴스화 될 때까지 검사가 지연된다.

std::complex c1(5, 6);
std::complex c2(4, 6);

std::max(c1,c2); //error in compile time!
//  since operator< or operator> is not defined for std::complex

3.1.4 타입 혼합하기

인수가 다른 두 가지 타입을 max에 사용하면 컴파일 에러가 발생한다. 일반적인 함수는 적절한 암시적 형변환을 하지만, 템플릿 함수는 암시적 형변환을 허용하지 않는다.

따라서 입력 인수를 직접 형변환을 하던지, 템플릿 함수에 명시적으로 템플릿 타입을 지정해야 한다.

unsigned u1 = 2;
int i = 3;
std::(u1, i); //error in compile time!
std::(int(u1), i); //OK!
std::max<int>(u1, i); //OK

템플릿 함수는 모든 타입에 대해 조합해 코드를 만들기 때문에 실행 시간 관점에서 효율적이다. 단, 모든 타입에 대한 정의가 필요하기 때문에 실행 파일의 용량이 커진다. (개인적인 의견: 컴파일 시간이 길어진다+hpp로 작성해야 한다).

3.1.5 유니폼 초기화

(생략)

3.1.6 자동 return 타입

C++ 14부터는 컴파일러에서 리턴 타입을 추론할 수 있다. 만약 함수 내에 여러 개의 return문이 있다면 추론 타입이 모두 동일해야 한다.

template<typename T, typename U>
inline auto max(T a, U b){
    return a>b?a:b;
}
profile
큰일날 사람

0개의 댓글