비-형식 템플릿 매개변수에 대한 설명
이 글은 이전의 템플릿에 관한 글들과 연결되어 있습니다.
시작하기 전에 '함수 템플릿 ( function template )에 대한 설명 1'을 읽어 보길 바랍니다.
템플릿의 정의에서 형식 템플릿 매개변수( type template parameter )가 임의의 타입이 들어갈 위치를 표시한다면, 비-형식 템플릿 매개변수는 컴파일 시 값을 알 수 있는 표현식으로 대체될 자리를 표시( placeholder )하는, 고정된 타입의 매개변수입니다.
#include <iostream>
#include <cmath> // for std::pow
using namespace std;
template< int N > // 비형식 템플릿 매개변수
int pow2(){
int val = std::pow(N, 2);
return val;
}
int main(){
pow2<3>; // ok
pow2<5>; // ok
int rv = 5; // 실시간 변수
pow2<rv>; // error!
constexpr int n = 5; // 컴파일 시 상수
int val = pow2<n>(); // ok
cout << "double-powerd value: " << val << endl;
}
위에서 함수 템플릿에 선언된 N이 비-형식 템플릿 매개변수입니다.
그리고, 이러한 매개변수를 가진 템플릿으로부터 함수를 생성하려면, 각괄호 <> 안에 임의의 데이터의 타입을 선언하는 것이 아니라, 고정된 타입( 여기서는 int )의 값을 나타내는 표현식을 사용해야 합니다.
위 예문에서와 같이, 컴파일러가 pow2<3> 선언을 만나게 되면, 함수 템플릿으로부터 다음과 같은 함수를 구체화( instantiation )합니다.
template<> // 함수 템플릿으로부터 구체화되었음
int pow2<3>(){
int val = std::pow(3,2); // val의 값은 9
return val;
}
이 구체화 과정에서, 컴파일러는 N 으로 표시되어 있는 기호를 표현식의 값 3 으로 대체하는 것입니다.
그리고, 프로그램 실행 시, 이렇게 만들어진 함수 pow2<3>() 를 호출하게 됩니다.
마찬가지로, pow2<5> 는 다음과 같이 구체화됩니다.
template<>
int pow2<5>(){ // 함수의 이름은 pow2<5>
int val = std::pow(5,2);
return val;
}
여기서 주목할 것은, 템플릿 함수를 선언 시, 비-형식 템플릿 인수가 달라지면 그에 따라 생성되는 템플릿 함수의 이름도 달라진다는 것입니다.
즉, pow2<3>() 과 pow2<5>() 는 형태만 같은, 별개의 함수입니다.
그러기 때문에, 당연히 함수 중복 오류도 발생하지 않습니다.
하지만, 다음과 같은 문장은 오류입니다.
int rv = 5; // 실시간 표현식
pow2<rv>; // error !
템플릿 함수를 생성하는 시기가 컴파일을 하는 때라는 것을 알고 있을 겁니다.
하지만, 위의 변수 rv 의 값은 실행 시가 되어서야 알 수 있습니다. ( 실행 시에 변수가 메모리에 생성됩니다. )
그러므로, 컴파일러는 이 표현식으로는 템플릿 함수를 생성할 수 없습니다.
이런 이유로, 비-형식 템플릿 매개변수가 사용된 위치를 대체할 표현식은, 컴파일 시에 그 값을 알 수 있는 표현식이어야 하고, 이 표현식을 상수 표현식( const expression )이라고 합니다.
그리고, 이러한 상수 표현식은 컴파일 시 상수와 컴파일 시 값을 알 수 있는 함수들로 구성됩니다.
constexpr int n = 5; // 컴파일 시 상수
int val = pow2<n>();
cout << "doubled value: " << val << endl;
▼출력
doubled value: 25
위 예문에서, 컴파일 시 상수인 constexpr 변수 n 이 상수 표현식의 예입니다.
컴파일러는 컴파일 시, n 의 값이 5라는 것을 알고 있으므로, pow2<n> 선언을 만나게 되면, pow2<5> 함수를 생성하게 될 것입니다.
상수 표현식을 나타내는 constexpr 키워드에 관한 내용은 여기서 볼 수 있습니다.
클래스 템플릿에서의 비-형식 템플릿 매개변수
함수 템플릿뿐만 아니라, 클래스 템플릿에서도 비-형식 템플릿 매개변수를 사용할 수 있습니다.
std::array가 대표적인 예입니다.
이 클래스 템플릿의 템플릿 매개변수 선언( template parameter declaration )을 보면 다음과 같이 정의되어 있습니다.
// std::array에서 발췌
template<typename _Tp, std::size_t _Nm>
이 array 클래스 템플릿이 C 타입의 배열과 동일한 기능을 수행하기 위해선, 컴파일 시 원소의 타입과 개수를 알아야 합니다.
그렇게 하기 위해선, 형식 템플릿 매개변수뿐만 아니라, 비-형식 템플릿 매개변수를 사용해서 컴파일 시 원소를 개수를 컴파일러에게 알려줘야 합니다.
( 함수의 매개변수는 상수 표현식이 될 수 없습니다. 따라서 컴파일 시에 함수를 통해서 원소의 개수를 전달할 수는 없습니다. )
이러한, array 클래스 객체를 생성하려면 다음과 같이 할 수 있습니다.
#include <array>
int c_arr[] = { 1,2,3,4,5 }; // c-style array
std::array< int, 5 > arr0{ 1, 2, 3, 4, 5 };
그리고, 이전 항목에서 작성했던 함수 템플릿 pow2를 '컴파일 시 상수( constexpr )'로 변경하면, 이를 이용해서도 array 클래스를 생성할 수 있습니다.
template< int N >
constexpr int pow2(){ // 컴파일 시 상수로 선언
int val = std::pow(N, 2);
return val;
}
int main(){
std::array<int, pow2<2>() > arr;
int cnt = 0;
for( auto& x : arr){ // 범위기반 for
x = cnt++;
}
for( int i = 0, n = arr.size(); i < n; i++){
cout << arr[i] << " ";
}
cout << endl;
}
▼출력
0 1 2 3
위에서, 컴파일 시 pow2<2>() 표현식의 값을 4 로 평가할 수 있습니다.
따라서, 컴파일러는 std::array 템플릿 정의에서 클래스를 생성할 때, 비-형식 템플릿 매개변수 _Nm 를 이 값으로 대체합니다.
이 글과 관련있는 글들
함수 템플릿의 특수화( function template specialization )
'C, C++ > C++ 언어' 카테고리의 다른 글
[C++] 클래스 템플릿의 특수화( class template specialization ) (0) | 2024.10.23 |
---|---|
[C++] 함수 템플릿의 특수화( function template specialization ) (0) | 2024.10.18 |
[C++] 클래스 템플릿( class template )에 대한 설명 (0) | 2024.10.15 |
[C++] 함수 템플릿( function template )에 대한 설명 2 (0) | 2024.10.13 |
[C++] 함수 템플릿 ( function template )에 대한 설명 1 (1) | 2024.10.13 |