Template arguments
テンプレートがインスタンス化されるためには、すべての template parameter が対応するテンプレート引数で置き換えられなければなりません。引数は明示的に指定されるか、推論されるか、デフォルト値が使用されます。
各 template-parameter-list 内のパラメータ( template identifier syntax を参照)は以下のカテゴリのいずれかに属します:
- 定数テンプレート引数
- 型テンプレート引数
- テンプレートテンプレート引数
目次 |
定数テンプレート引数
別名 非型テンプレート引数 ( 後述 を参照)。
|
定数テンプレートパラメータで使用できるテンプレート引数は、あらゆる 明示的に定数評価される式 を使用できます。 |
(C++11まで) |
|
定数テンプレートパラメータで使用できるテンプレート引数は、あらゆる 初期化子節 を使用できます。初期化子節が式である場合、それは 明示的に定数評価される 必要があります。 |
(C++11以降) |
定数テンプレートパラメータ宣言の
type
が
T
として指定され、そのパラメータに与えられたテンプレート引数が
E
である場合。
|
発明された宣言 T x = E ; は、 constexpr 変数 の定義に対する意味的制約を、 静的記憶域期間 で満たさなければならない。 |
(C++20以降) |
|
推定されたパラメータ型が 構造的型 でない場合、プログラムは不適格である。 プレースホルダ型を使用する型を持つ定数テンプレートパラメータパックについては、型は各テンプレート引数に対して独立して推定され、一致する必要はない。 |
(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; // OK 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; // エラー: テンプレートパラメータオブジェクトが // 導入された一時オブジェクトとテンプレート引数等価ではありません
|
定数テンプレートパラメータを持つテンプレートをインスタンス化する際には、以下の制限が適用されます:
特に、これは文字列リテラル、配列要素のアドレス、および非静的メンバのアドレスが、対応する定数テンプレートパラメータがオブジェクトへのポインタであるテンプレートをインスタンス化するためのテンプレート引数として使用できないことを意味します。 |
(C++17まで) |
|
参照型またはポインタ型の定数テンプレートパラメータ およびクラス型の定数テンプレートパラメータとその部分オブジェクトにおける参照型またはポインタ型の非静的データメンバ (C++20以降) は、以下を参照/アドレスとすることはできません: |
(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; // OK: 静的メンバーのアドレス X<&S::s> x6; // OK: 静的メンバーのアドレス template<const int& CRI> struct B {}; B<1> b2; // エラー: テンプレート引数に一時オブジェクトが必要となる int c = 1; B<c> b1; // OK
型テンプレート引数
型テンプレートパラメータに対するテンプレート引数は、 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
は、以下の2つの関数テンプレートへの書き換えを行った場合、
関数テンプレート
に対する部分順序付けルールに従って、
P
に対応する関数テンプレートが
A
に対応する関数テンプレートと少なくとも同等に特殊化されている場合、テンプレートテンプレート引数
A
に対して
少なくとも同等に特殊化されている
と見なされます。
A
のテンプレートパラメータリスト(デフォルト引数を含む)を持つ架空のクラステンプレート
X
を仮定します:
-
2つの関数テンプレートはそれぞれ、
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の2番目のパラメータは型パラメータパックであるが、 // 17は型を指定していないため eval<E<int, float>> eE; // エラー: Eは部分特殊化のTTにマッチしない // Eの3番目(デフォルト)パラメータは定数であるため
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; // OK X<B> xb; // P0522R0以降OK // 以前はエラー: 完全一致しない X<C> xc; // P0522R0以降OK // 以前はエラー: 完全一致しない template<template<class...> class Q> class Y { /* ... */ }; Y<A> ya; // OK Y<B> yb; // OK Y<C> yc; // OK template<auto n> class D { /* ... */ }; // 注: C++17 template<template<int> class R> class Z { /* ... */ }; Z<D> zd; // P0522R0以降OK: テンプレートパラメータが // テンプレート引数よりも特殊化されている template<int> struct SI { /* ... */ }; template<template<auto> class> void FA(); // 注: C++17 FA<SI>(); // エラー
テンプレート引数の等価性
テンプレート引数の等価性は、2つの テンプレート識別子 が同一であるかどうかを判断するために使用されます。
2つの値は、同じ型であり、かつ以下の条件のいずれかを満たす場合、 template-argument-equivalent である:
- それらが整数型または列挙型であり、その値が同じである場合。
- それらがポインタ型であり、同じポインタ値を持つ場合。
- それらがメンバへのポインタ型であり、同じクラスメンバを参照しているか、両方がヌルメンバポインタ値である場合。
- それらが左辺値参照型であり、同じオブジェクトまたは関数を参照している場合。
|
(C++11以降) |
|
(C++20以降) |
曖昧性解決
テンプレート引数が type-id と式の両方として解釈可能な場合、対応するテンプレートパラメータが定数であっても、常に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. 明確化された |