여러개의 형식 템플릿 매개변수를 가진 함수 템플릿
이 글은 "함수 템플릿( function template )에 대한 설명 1"에 연결되는 글입니다.
이전 글에서 add 함수 템플릿을 정의했습니다.
template < typename T > // 함수 템플릿
T add ( T a, T b){
return a + b;
}
그리고, add( 2, 5 ) 를 호출하면, 컴파일러은 이 함수 템플릿을 사용해, 다음과 같은 함수를 생성하고 호출할 것입니다.
template<>
int add ( int a, int b){
return a + b;
}
그런데, 만약, add( 2, 3.14 ) 함수를 호출하면, 이 함수 템플릿은 어떤 함수를 생성할까요?
아마 가장 가능성 있는 예상은 다음의 템플릿 함수일 것입니다.
template<>
double add( double a, double b){
return a + b;
}
double 타입이 인수 2 와 3.14 를 모두 담을 수 있기 때문입니다.
그렇지만, 놀랍게도 add( 2, 3.14 ) 의 함수 호출은 오류를 발생시킵니다.
template < typename T >
T add ( T a, T b){
return a + b;
}
...
int ret = add( 2, 3.14); // error !!
▼출력
error: no matching function for call to 'add(int, double)'
이것은 컴파일러가 함수의 인자들로부터 적절한 타입을 추론하지 못해서, 템플릿 함수를 구체화( instantiation )하지 못했기 때문에 발생한 오류입니다.
위에서 했던 예상은, "컴파일러가 값 2 의 타입인 int를 암시적으로 double 타입으로 변환하고, 이 double 타입의 인수를 처리할 수 있는 템플릿 함수를 생성할 것이다"라는 것이었을 겁니다.
그러나, 컴파일러는 실제 구현된 함수를 보기 전까지는, 주어지는 인수를 다른 타입으로 변환할 수 없습니다.
따라서, int를 double를 변환할 수 없고, 그 결과 형식 템플릿 매개 변수를 추론할 수 없었던 것입니다.
( int와 double 둘 중의 하나로 추론해야 되는데, 아무런 힌트도 없습니다. )
이 문제를 해결할려면, 우선 함수 인수의 타입이 일치해야 한다는 것을 알았습니다.
따라서, 다음과 같이 하면 원하는 값을 구할 수 있습니다.
int ret = add( static_cast<double>(2), 3.14); // ok
그러나, 이러한 형태의 코드는 너무 불만족스럽습니다.
다른 방식으로는, 명시적으로 형식 템플릿 매개변수를 지정하는 방법이 있습니다.
int ret = add< double >( 2, 3.14); // 명시적인 템플릿 인수 지정
이렇게 템플릿 인수( template argument )를 지정해서, 함수 템플릿을 호출하면, 컴파일러는 double 타입의 템플릿의 함수를 작성할 것입니다.
이제, 함수가 존재하므로, 컴파일러는 인수 2 의 타입을 암시적으로 double 타입으로 변환합니다.
그리고, 성공적인 함수 호출을 하게 될 것입니다.
그런데, 이렇게 항상 템플릿 인수를 지정하기 위해, 인수의 타입에 신경을 써야 하는 것이 맘에 걸립니다.
그리고, 애초에 이러한 문제가 발생한 원인은 서로 다른 인수의 타입을 하나( 여기서는 T )로 통일해야 한다는 전제 때문입니다.
만약, 이러한 전제를 무시한다면, 서로 다른 타입의 인수를 함수에 전달해, 함수 내에서 처리하는 방법을 생각할 수 있습니다.
그리고, 이러한 사고 방식이 합리적입니다.
다음의 예문이 이렇게 문제를 푸는 방법입니다.
// 두 개의 타입을 처리하는 함수 템플릿
template< typename T, typename U >
T add( T a, U b){
return a + b;
}
int main(){
double ret = add( 2, 3.14 ); // 인수의 타입에 자유로운 함수 호출
cout << "ret: " << ret << endl;
}
▼출력
ret: 5
위의 함수 템플릿은 서로 다른 두 개의 타입을 받는 템플릿입니다.
컴파일러는 인수 2 로부터 int 타입을, 3.14 로부터는 double 타입을 추론합니다.
그리고, 다음과 같은 함수를 구체화합니다.
template<>
int add( int a, double b){
return a + b;
}
그런데, 아직 해결해야 할 문제가 남아 있습니다.
형식 템플릿 매개변수 T 가 int 타입으로 대체되기 때문에, 함수의 반환 값이 int 타입이 된다는 것입니다.
그래서, 결과가 5.14 가 되어야 되는데, 5 가 되었습니다.
그리고, ' 그럼 3.14 를 먼저 전달하면 되지 않을까'라고 생각하는 것은 전혀 문제가 해결되지 않습니다.
이런 방법은 위에서 제시했던 방법들보다 더 나쁜 방법입니다.
그럼, 어떻게 반환 값의 타입을 결정해야 할까요?
이럴때는, 컴파일러가 매개 변수의 타입을 추론하는 것과 같이, 이것조차 컴파일러가 결정하라고 떠넘기는 것입니다.
template< typename T, typename U >
auto add( T a, U b){
return a + b;
}
이제, 컴파일러는 함수 반환 값의 타입을 double로 추론할 것입니다.
이것은 a 또는 b 가 double 타입으로 형 변환되고, 변환 후의 a+b 의 계산 값은 double 타입일 것이기 때문입니다.
// 함수 템플릿이 구체화한 함수 ( 눈에는 보이지 않음 )
template <>
double add( int a, double b ){
return a + b;
}
int main(){
double ret = add( 2, 3.14 );
cout << "ret: " << ret << endl;
}
▼출력
ret: 5.14
함수 템플릿의 오버로딩( overloading )
함수 템플릿도 일반 함수와 같이 오버로딩 할 수 있습니다.
즉, 함수 템플릿의 이름이 동일하더라도 함수 매개변수의 수와 타입이 다르면, 중복된 정의로 보지 않는다는 것입니다.
template < typename T >
T add ( T a, T b){
cout << "one type template parameter\n";
return a + b;
}
template< typename T, typename U >
auto add( T a, U b ){
cout << "two type template parameters\n";
return a + b;
}
그리고, 이러한 함수 템플릿이 있는 경우, 이전 항목에서 설명한 것과 같이, add( 2, 3.14 ) 함수 호출은 위에서 두 번째의 함수 템플릿을 통해 만들어지는 함수를 호출할 것입니다.
그럼, add( 3, 5 ) 를 호출하면, 컴파일러는 위의 둘 중에서 어떤 함수 템플릿을 이용할까요?
int main(){
add( 2, 3.14 ); // add<T, U> 함수 템플릿 이용
add( 3, 5 ); // ??
}
▼출력
two type template parameters
one type template parameter
main 함수의 마지막 함수 호출 add( 3, 5 ) 는 위 두 개의 함수 템플릿 모두로부터 함수를 만들고, 호출할 수 있습니다.
그렇지만, 이런 경우 컴파일러는 적은 수의 매개변수를 가진 첫 번째 함수 템플릿을 선호합니다.
template<> // 첫 번째 함수 템플릿으로부터 구체화된 함수
int add ( int a, int b){
cout << "one type template parameter\n";
return a + b;
}
이전 글에서 템플릿 함수보다 일반 함수를 선호했던 것과 마찬가지로, 컴파일러는 타입의 선택이 제한적인 템플릿 함수를 선호합니다.
그 결과 다음 같은 출력을 하게 되는 것입니다.
one type template parameter
.
이 글과 관련있는 글들
클래스 템플릿( class template )에 대한 설명
'C, C++ > C++ 언어' 카테고리의 다른 글
[C++] 비-형식 템플릿 매개변수( non-type template parameter) (0) | 2024.10.18 |
---|---|
[C++] 클래스 템플릿( class template )에 대한 설명 (0) | 2024.10.15 |
[C++] 함수 템플릿 ( function template )에 대한 설명 1 (1) | 2024.10.13 |
[C++] 포인터 this의 이해 (2) | 2024.10.07 |
[C++] 함수 삭제( = delete )를 통한 기능 제어 (1) | 2024.10.05 |