함수 템플릿은 잠재적으로 무한한 수의 함수 오버로드를 생성하는 청사진이다.
template<typename T>
T max(T a, T b){
if (a > b)
return a;
else
return b;
}
이 함수 템플릿은 오버로드를 치환하며, 함수 이름은 그대로 max
다.
컴파일러는 제네릭이 아닌 함수의 경우 정의를 읽고 오류를 검사한 뒤 실행 가능한 코드를 생상한다. 제네릭 함수의 경우에는 구문 분석 오류처럼 템플릿 매개변수기 독립적인 오류만 컴출할 수 있다.
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;
}
위 코드는 컴파일된다. 이 함수는 T
가 int
나 double
같은 타입으로는 호출할 수 없다.
함수 템플릿 자체만 컴파일하더라도 바이너리에서 코드를 생성하지 않는다. 우리가 함수 템플릿을 호출할 때만 코드를 생성한다. 이 경우, 우리는 이 함수 템플릿을 인스턴스화 한다. 그런 다음 컴파일러는 제네릭 함수가 주어진 인수 타입에 대해 올바른지 여부를 철저히 검사한다.
여기서 좀 더 명시적으로 템플릿 매개변수를 지환하는 타입을 선언하는 방법도 있다.
std::cout << max<float>(8.1, 9.3) << '\n';
이 방법을 사용하면 함수 호출 없이 인스턴스화를 강제할 수 있다. 이는 오브젝트 파일을 생성할 때 유용할 수 있다.
함수 템플릿의 매개변수도 const
와 레퍼런스로 한정될 수 있다.
template<typename T>
T max(const T& a, const T& b) {
...
}
아래와 같이 모든 인수를 받을 수 있도록 상수 레퍼런스를 매개변수로 사용할 수 있다.
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
변수를 전달할 때 TPara
는 int
로 치환되므로 p
의 타입은 int&
며 i
를 참조한다.
j
와 같이 변경 가능한 레퍼런스 변수를 전달할 때 동일한 치환 과정을 거친다.
k
와 같이 const int
또는 const int&
를 전달하면 TPara
가 const int
로 지환되는 것은 가능하다. 따라서 p
는 const int
가 된다.
template<typename TPara>
void f4(TPara&& p) {
...
}
포워드 레퍼런스가 Rvalue, Lvalue 모두 받아들일 수 있다. 예를들면,
f4(3);
f4(move(i));
f4(move(up));
위 코드에서 TPara
는 int
와 unique_ptr<int>
로 치환되고 p
는 해당 인수 타입의 Rvalue 레퍼런스다.
i
나 j
같은 Lvalue를 사용해 f4
를 호출하면 컴파일러는 이러한 인수를 템플릿 Rvalue 레퍼런스 매개변수로 사용한다.
우리는 move
를 통해 Lvalue를 Rvalue로 변환할 수 있음을 알고있다. forward
를 이용하면 Rvalue 레퍼런스를 Rvalue로, Lvalue 레퍼런스를 Lvalue로 변환한다.
비교 연산자가 정의되지 않은 인수에 대한 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
인수가 다른 두 가지 타입을 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로 작성해야 한다).
(생략)
C++ 14부터는 컴파일러에서 리턴 타입을 추론할 수 있다. 만약 함수 내에 여러 개의 return
문이 있다면 추론 타입이 모두 동일해야 한다.
template<typename T, typename U>
inline auto max(T a, U b){
return a>b?a:b;
}