Template arguments
템플릿이 인스턴스화되기 위해서는 모든 템플릿 매개변수 가 해당 템플릿 인수로 대체되어야 합니다. 인수는 명시적으로 제공되거나, 추론되거나, 기본값이 사용됩니다.
template-parameter-list ( template identifier syntax 참조)의 각 매개변수는 다음 범주 중 하나에 속합니다:
- 상수 템플릿 인수
- 타입 템플릿 인수
- 템플릿 템플릿 인수
목차 |
상수 템플릿 인수
비타입 템플릿 인자 로도 알려져 있습니다 (자세한 내용은 아래 참조).
|
상수 템플릿 매개변수와 함께 사용할 수 있는 템플릿 인자는 모든 manifestly constant-evaluated expression 이 될 수 있습니다. |
(until C++11) |
|
상수 템플릿 매개변수와 함께 사용할 수 있는 템플릿 인자는 모든 initializer clause 가 될 수 있습니다. 초기화 절이 표현식인 경우, 반드시 manifestly constant-evaluated 되어야 합니다. |
(since C++11) |
주어진
type
의
상수 템플릿 매개변수 선언
을
T
로 하고, 이 매개변수에 제공된 템플릿 인자를
E
로 합니다.
|
발명된 선언 T x = E ; 는 constexpr 변수 의 정의를 위한 의미론적 제약 조건을 정적 저장 기간 으로 만족해야 합니다. |
(C++20부터) |
|
만약
추론된 매개변수 타입이 구조적 타입 이 아닌 경우, 프로그램은 ill-formed입니다. 플레이스홀더 타입을 사용하는 타입을 가진 상수 템플릿 매개변수 팩의 경우, 타입은 각 템플릿 인수에 대해 독립적으로 추론되며 일치할 필요가 없습니다. |
(C++17부터) |
template<auto n> struct B { /* ... */ }; B<5> b1; // OK: 상수 템플릿 매개변수 타입은 int B<'a'> b2; // OK: 상수 템플릿 매개변수 타입은 char B<2.5> b3; // 오류 (C++20 이전): 상수 템플릿 매개변수 타입은 double이 될 수 없음 // C++20에서 추론된 클래스 타입 플레이스홀더, 클래스 템플릿 인자는 // 호출 지점에서 추론됨 template<std::array arr> void f(); f<std::array<double, 8>{}>(); template<auto...> struct C {}; C<'C', 0, 2L, nullptr> x; // OK
상수 템플릿 매개변수
P
의 값은
(추론된 가능성이 있는)
(C++17부터)
타입
T
로부터 다음과 같이 템플릿 인수
A
에 의해 결정됩니다:
|
(C++11 이전) |
|
(C++11 이후)
(C++20 이전) |
|
(C++20 이후) |
template<int i> struct C { /* ... */ }; C<{42}> c1; // 정상 template<auto n> struct B { /* ... */ }; struct J1 { J1* self = this; }; B<J1{}> j1; // 오류: 템플릿 매개변수 객체의 초기화가 // 상수 표현식이 아님 struct J2 { J2 *self = this; constexpr J2() {} constexpr J2(const J2&) {} }; B<J2{}> j2; // 오류: 템플릿 매개변수 객체가 // 도입된 임시 객체와 템플릿 인자 동등하지 않음
|
상수 템플릿 매개변수를 가진 템플릿을 인스턴스화할 때 다음과 같은 제한 사항이 적용됩니다:
특히, 이는 문자열 리터럴, 배열 요소의 주소, 비정적 멤버의 주소가 객체 포인터인 해당 상수 템플릿 매개변수를 가진 템플릿을 인스턴스화하는 데 템플릿 인자로 사용될 수 없음을 의미합니다. |
(until C++17) |
|
참조 또는 포인터 타입의 상수 템플릿 매개변수 및 클래스 타입의 상수 템플릿 매개변수와 그 하위 객체에서 참조 또는 포인터 타입의 비정적 데이터 멤버 (since C++20) 는 다음을 참조하거나 그 주소일 수 없습니다: |
(since C++17) |
template<const int* pci> struct X {}; int ai[10]; X<ai> xi; // OK: 배열에서 포인터로의 변환 및 cv-한정자 변환 struct Y {}; template<const Y& b> struct Z {}; Y y; Z<y> z; // OK: 변환 없음 template<int (&pa)[5]> struct W {}; int b[5]; W<b> w; // OK: 변환 없음 void f(char); void f(int); template<void (* pf)(int)> struct A {}; A<&f> a; // OK: 오버로드 해결이 f(int)를 선택함
template<class T, const char* p> class X {}; X<int, "Studebaker"> x1; // 오류: 템플릿 인자로 문자열 리터럴 사용 template<int* p> class X {}; int a[10]; struct S { int m; static int s; } s; X<&a[2]> x3; // 오류 (C++20 이전): 배열 요소의 주소 X<&s.m> x4; // 오류 (C++20 이전): 비정적 멤버의 주소 X<&s.s> x5; // 정상: 정적 멤버의 주소 X<&S::s> x6; // 정상: 정적 멤버의 주소 template<const int& CRI> struct B {}; B<1> b2; // 오류: 템플릿 인자에 임시 객체가 필요함 int c = 1; B<c> b1; // 정상
타입 템플릿 인수
타입 템플릿 매개변수에 대한 템플릿 인수는 type-id 여야 하며, 이는 불완전한 타입을 명시할 수 있습니다:
template<typename T> class X {}; // 클래스 템플릿 struct A; // 불완전 타입 typedef struct {} B; // 이름 없는 타입에 대한 타입 별칭 int main() { X<A> x1; // OK: 'A'는 타입을 나타냄 X<A*> x2; // OK: 'A*'는 타입을 나타냄 X<B> x3; // OK: 'B'는 타입을 나타냄 }
템플릿 템플릿 인수
템플릿 템플릿 매개변수에 대한 템플릿 인자는 반드시 id-expression 이어야 하며, 이는 클래스 템플릿이나 템플릿 별칭을 지정해야 합니다.
인자가 클래스 템플릿인 경우, 매개변수와 매칭할 때는 기본 템플릿만 고려됩니다. 부분 특수화가 존재하는 경우, 이 템플릿 템플릿 매개변수를 기반으로 한 특수화가 실제로 인스턴스화될 때만 고려됩니다.
template<typename T> // 기본 템플릿 class A { int x; }; template<typename T> // 부분 특수화 class A<T*> { long x; }; // 템플릿 템플릿 매개변수 V를 가진 클래스 템플릿 template<template<typename> class V> class C { V<int> y; // 기본 템플릿 사용 V<int*> z; // 부분 특수화 사용 }; C<A> c; // c.y.x는 int 타입, c.z.x는 long 타입
템플릿 템플릿 인수
A
를 템플릿 템플릿 매개변수
P
와 매칭시키기 위해서는,
P
가
A
보다
최소한 동등하게 특수화되어야
합니다 (아래 참조).
P
의 매개변수 목록에
파라미터 팩
이 포함된 경우,
A
의 템플릿 매개변수 목록에서 0개 이상의 템플릿 매개변수(또는 파라미터 팩)가 이와 매칭됩니다.
(C++11부터)
공식적으로, 템플릿 템플릿 매개변수
P
는 다음과 같은 두 함수 템플릿으로 재작성할 때,
함수 템플릿
에 대한 부분 순서 규칙에 따라
P
에 해당하는 함수 템플릿이
A
에 해당하는 함수 템플릿보다
최소한 동등하게 특수화되었거나 더 특수화된 경우
, 템플릿 템플릿 인수
A
보다
최소한 동등하게 특수화되었거나 더 특수화된
것으로 간주합니다.
A
의 템플릿 매개변수 목록(기본 인수 포함)을 가진 가상의 클래스 템플릿
X
가 주어졌을 때:
-
두 함수 템플릿은 각각
P또는A와 동일한 템플릿 매개변수를 가집니다. -
각 함수 템플릿은 해당 함수 템플릿의 템플릿 매개변수에 대응하는 템플릿 인자로 특수화된
X타입의 단일 함수 매개변수를 가지며, 여기서 함수 템플릿의 템플릿 매개변수 목록에 있는 각 템플릿 매개변수PP에 대해 대응하는 템플릿 인자AA가 형성됩니다. 만약PP가 매개변수 팩을 선언한다면,AA는 팩 확장PP...입니다; 그렇지 않으면, (C++11부터)AA는 id-표현식PP입니다.
만약 재작성 결과가 유효하지 않은 타입을 생성한다면,
P
는
A
만큼 특수화되지 않은 것입니다.
template<typename T> struct eval; // 기본 템플릿 template<template<typename, typename...> class TT, typename T1, typename... Rest> struct eval<TT<T1, Rest...>> {}; // eval의 부분 특수화 template<typename T1> struct A; template<typename T1, typename T2> struct B; template<int N> struct C; template<typename T1, int N> struct D; template<typename T1, typename T2, int N = 17> struct E; eval<A<int>> eA; // OK: eval의 부분 특수화와 일치 eval<B<int, float>> eB; // OK: eval의 부분 특수화와 일치 eval<C<17>> eC; // 오류: C가 부분 특수화의 TT와 일치하지 않음 // TT의 첫 번째 매개변수는 타입 템플릿 매개변수인 반면 // 17은 타입을 명시하지 않음 eval<D<int, 17>> eD; // 오류: D가 부분 특수화의 TT와 일치하지 않음 // TT의 두 번째 매개변수는 타입 매개변수 팩인 반면 // 17은 타입을 명시하지 않음 eval<E<int, float>> eE; // 오류: E가 부분 특수화의 TT와 일치하지 않음 // E의 세 번째(기본) 매개변수는 상수임
P0522R0
가 채택되기 전에는,
A
의 각 템플릿 매개변수가
P
의 해당 템플릿 매개변수와 정확히 일치해야 했습니다. 이로 인해 많은 합리적인 템플릿 인수가 수용되지 못하는 문제가 있었습니다.
이 문제는 매우 일찍 지적되었지만( CWG#150 ), 해결될 때쯤에는 변경 사항들이 C++17 작업 문서에 적용되어 해당 해결책이 사실상 C++17 기능이 되었습니다. 많은 컴파일러들이 기본적으로 이를 비활성화합니다:
- GCC 는 C++17 이전의 모든 언어 모드에서 기본적으로 이를 비활성화하며, 이러한 모드에서는 컴파일러 플래그를 설정해야만 활성화할 수 있습니다.
- Clang 는 모든 언어 모드에서 기본적으로 이를 비활성화하며, 컴파일러 플래그를 설정해야만 활성화할 수 있습니다.
- Microsoft Visual Studio 는 이를 일반적인 C++17 기능으로 취급하며 C++17 이상의 언어 모드에서만 활성화합니다(즉, 기본 모드인 C++14 언어 모드에서는 지원되지 않음).
template<class T> class A { /* ... */ }; template<class T, class U = T> class B { /* ... */ }; template<class... Types> class C { /* ... */ }; template<template<class> class P> class X { /* ... */ }; X<A> xa; // 정상 X<B> xb; // P0522R0 이후 정상 // 이전에는 오류: 정확히 일치하지 않음 X<C> xc; // P0522R0 이후 정상 // 이전에는 오류: 정확히 일치하지 않음 template<template<class...> class Q> class Y { /* ... */ }; Y<A> ya; // 정상 Y<B> yb; // 정상 Y<C> yc; // 정상 template<auto n> class D { /* ... */ }; // 참고: C++17 template<template<int> class R> class Z { /* ... */ }; Z<D> zd; // P0522R0 이후 정상: 템플릿 매개변수가 // 템플릿 인수보다 더 특수화됨 template<int> struct SI { /* ... */ }; template<template<auto> class> void FA(); // 참고: C++17 FA<SI>(); // 오류
템플릿 인자 동등성
템플릿 인자 동등성은 두 개의 템플릿 식별자 가 동일한지 결정하는 데 사용됩니다.
두 값은 동일한 타입이고 다음 조건 중 하나를 만족하는 경우 template-argument-equivalent 입니다:
- 정수형이나 열거형이며 그 값이 동일한 경우
- 포인터 타입이며 동일한 포인터 값을 가지는 경우
- 멤버 포인터 타입이며 동일한 클래스 멤버를 참조하거나 둘 다 null 멤버 포인터 값인 경우
- lvalue 참조 타입이며 동일한 객체나 함수를 참조하는 경우
|
(C++11부터) |
|
(C++20부터) |
모호성 해결
템플릿 인자가 type-id 와 표현식(expression) 모두로 해석될 수 있는 경우, 해당 템플릿 매개변수가 상수(constant)라 하더라도 항상 type-id로 해석됩니다:
template<class T> void f(); // #1 template<int I> void f(); // #2 void g() { f<int()>(); // "int()"는 타입이면서 표현식입니다, // 타입으로 해석되므로 #1을 호출합니다 }
참고 사항
C++26 이전에는, 상수 템플릿 인수가 표준 용어에서 비타입 템플릿 인수라고 불렸습니다. 이 용어는 P2841R6 / PR #7587 에 의해 변경되었습니다.
| 기능 테스트 매크로 | 값 | 표준 | 기능 |
|---|---|---|---|
__cpp_template_template_args
|
201611L
|
(C++17)
(DR) |
템플릿 템플릿 인자 의 매칭 |
__cpp_nontype_template_args
|
201411L
|
(C++17) | 모든 상수 템플릿 인자 에 대한 상수 평가 허용 |
201911L
|
(C++20) | 상수 템플릿 매개변수 에서의 클래스 타입과 부동소수점 타입 |
예제
|
이 섹션은 불완전합니다
이유: 예시가 없음 |
결함 보고서
다음의 동작 변경 결함 보고서들은 이전에 발표된 C++ 표준에 소급 적용되었습니다.
| DR | 적용 대상 | 게시된 동작 | 올바른 동작 |
|---|---|---|---|
|
CWG 150
( P0522R0 ) |
C++98 |
템플릿-템플릿 인수가 템플릿-템플릿 매개변수의
매개변수 목록과 정확히 일치해야 했음 |
더 특수화된 경우도
허용됨 |
| CWG 354 | C++98 | 널 포인터 값을 상수 템플릿 인수로 사용할 수 없었음 | 허용됨 |
| CWG 1398 | C++11 |
상수 템플릿 인수가
std::nullptr_t
타입을 가질 수 없었음
|
허용됨 |
| CWG 1570 | C++98 | 상수 템플릿 인수가 하위 객체의 주소를 지정할 수 있었음 | 허용되지 않음 |
| P2308R1 |
C++11
C++20 |
1. 상수 템플릿 인수에 대해 목록 초기화가
허용되지 않았음 (C++11) 2. 클래스 타입의 상수 템플릿 매개변수가 어떻게 초기화되는지 불분명했음 (C++20) |
1. 허용됨
2. 명확히 규정됨 |