テンプレートパラメータとテンプレート引数
目次 |
[edit] テンプレートパラメータ
すべてのテンプレートは、テンプレート宣言構文のparameter-listで示される1つ以上のテンプレートパラメータによってパラメータ化されます。
template < parameter-list > declaration |
(1) | ||||||||
template < parameter-list > requires constraint declaration |
(2) | (C++20以降) | |||||||
parameter-list の各パラメータは、次のいずれかです。
- 非型テンプレートパラメータ;
- 型テンプレートパラメータ;
- テンプレートテンプレートパラメータ。
|
|
P2841 (Concept and variable-template template-parameters) は、「非型テンプレートパラメータ/引数」を「定数テンプレートパラメータ/引数」に意味を変更せずに名称変更しました。「非型テンプレートパラメータ」とその略語「NTTP」が広範に使用されているため、現在の用語は保持されています。 |
[edit] 非型テンプレートパラメータ
| type name(オプション) | (1) | ||||||||
type name(オプション) = default |
(2) | ||||||||
type ... name(オプション) |
(3) | (C++11以降) | |||||||
| type | - | 次の型のいずれか
| ||||
| name | - | 非型テンプレートパラメータの名前 | ||||
| default | - | デフォルトテンプレート引数 |
構造型とは、次の型のいずれかです (オプションで cv 修飾されますが、修飾子は無視されます)。
- 左辺値参照型 (オブジェクトまたは関数への参照);
- 整数型;
- ポインタ型 (オブジェクトまたは関数へのポインタ);
- メンバへのポインタ型 (メンバオブジェクトまたはメンバ関数へのポインタ);
- 列挙型;
| (C++11以降) |
|
(C++20以降) |
配列型と関数型はテンプレート宣言で記述できますが、それぞれ適切なオブジェクトへのポインタと関数へのポインタに自動的に置き換えられます。
クラステンプレートの本体内で非型テンプレートパラメータの名前が式で使用される場合、その型が左辺値参照型でない限り、変更不可能なprvalueです、またはその型がクラス型である場合を除いて(C++20以降)。
形式 class Foo のテンプレートパラメータは、たとえ class Foo がelaborated type specifierであり、class Foo x; が x を型 Foo のものと宣言していても、型 Foo の無名の非型テンプレートパラメータではありません。
|
クラス型 struct A { friend bool operator==(const A&, const A&) = default; }; template<A a> void f() { &a; // OK const A& ra = a, &rb = a; // Both bound to the same template parameter object assert(&ra == &rb); // passes } |
(C++20以降) |
[edit] 型テンプレートパラメータ
| type-parameter-key name(オプション) | (1) | ||||||||
type-parameter-key name(オプション) = default |
(2) | ||||||||
type-parameter-key ... name(オプション) |
(3) | (C++11以降) | |||||||
| type-constraint name(オプション) | (4) | (C++20以降) | |||||||
type-constraint name(オプション) = default |
(5) | (C++20以降) | |||||||
type-constraint ... name(オプション) |
(6) | (C++20以降) | |||||||
| type-parameter-key | - | typename または class のいずれか。型テンプレートパラメータ宣言においてこれらのキーワードに違いはありません。 |
| type-constraint | - | コンセプトの名前、またはコンセプトの名前の後にテンプレート引数リスト (山括弧内) が続くもの。どちらの場合も、コンセプト名はオプションで修飾できます。 |
| name | - | 型テンプレートパラメータの名前 |
| default | - | デフォルトテンプレート引数 |
template<class T> class My_vector { /* ... */ };
template<class T = void> struct My_op_functor { /* ... */ };
template<My_concept T> class My_constrained_vector { /* ... */ };
template<My_concept T = void> class My_constrained_op_functor { /* ... */ };
パラメータの名前はオプションです。
// Declarations of the templates shown above: template<class> class My_vector; template<class = void> struct My_op_functor; template<typename...> class My_tuple;
テンプレート宣言の本体では、型パラメータの名前は、テンプレートがインスタンス化されるときに提供される型の別名となる typedef-name です。
|
コンセプト
template<typename T> concept C1 = true; template<typename... Ts> // variadic concept concept C2 = true; template<typename T, typename U> concept C3 = true; template<C1 T> struct s1; // constraint-expression is C1<T> template<C1... T> struct s2; // constraint-expression is (C1<T> && ...) template<C2... T> struct s3; // constraint-expression is (C2<T> && ...) template<C3<int> T> struct s4; // constraint-expression is C3<T, int> template<C3<int>... T> struct s5; // constraint-expression is (C3<T, int> && ...) |
(C++20以降) |
[edit] テンプレートテンプレートパラメータ
template < parameter-list > type-parameter-key name(オプション) |
(1) | ||||||||
template < parameter-list > type-parameter-key name(オプション) = default |
(2) | ||||||||
template < parameter-list > type-parameter-key ... name(オプション) |
(3) | (C++11以降) | |||||||
| type-parameter-key | - | class または typename(C++17以降) |
テンプレート宣言の本体では、このパラメータの名前はテンプレート名です (インスタンス化には引数が必要です)。
template<typename T> class my_array {}; // two type template parameters and one template template parameter: template<typename K, typename V, template<typename> typename C = my_array> class Map { C<K> key; C<V> value; };
[edit] テンプレートパラメータの名前解決
テンプレートパラメータの名前は、そのスコープ内 (ネストされたスコープを含む) で再宣言することはできません。テンプレートパラメータはテンプレート名と同じ名前を持つことはできません。
template<class T, int N> class Y { int T; // error: template parameter redeclared void f() { char T; // error: template parameter redeclared } }; template<class X> class X; // error: template parameter redeclared
クラステンプレートの定義の外側に現れるクラステンプレートのメンバの定義では、クラステンプレートのメンバの名前は、囲んでいるクラステンプレートのテンプレートパラメータの名前を隠しますが、そのメンバがクラスまたは関数テンプレートである場合、そのメンバのテンプレートパラメータの名前は隠しません。
template<class T> struct A { struct B {}; typedef void C; void f(); template<class U> void g(U); }; template<class B> void A<B>::f() { B b; // A's B, not the template parameter } template<class B> template<class C> void A<B>::g(C) { B b; // A's B, not the template parameter C c; // the template parameter C, not A's C }
クラステンプレート定義を含む名前空間の外側に現れるクラステンプレートのメンバの定義では、テンプレートパラメータの名前はこの名前空間のメンバの名前を隠します。
namespace N { class C {}; template<class T> class B { void f(T); }; } template<class C> void N::B<C>::f(C) { C b; // C is the template parameter, not N::C }
クラステンプレートの定義、またはテンプレート定義の外側に現れるそのようなテンプレートのメンバの定義では、非依存基底クラスごとに、基底クラスの名前または基底クラスのメンバの名前がテンプレートパラメータの名前と同じ場合、基底クラスの名前またはメンバの名前はテンプレートパラメータの名前を隠します。
struct A { struct B {}; int C; int Y; }; template<class B, class C> struct X : A { B b; // A's B C b; // error: A's C isn't a type name };
[edit] テンプレート引数
テンプレートをインスタンス化するには、すべてのテンプレートパラメータ (型、非型、またはテンプレート) を対応するテンプレート引数に置き換える必要があります。クラステンプレートの場合、引数は明示的に提供されるか、イニシャライザから推論されるか、(C++17以降) またはデフォルト値が使用されます。関数テンプレートの場合、引数は明示的に提供されるか、コンテキストから推論されるか、またはデフォルト値が使用されます。
引数が型IDと式の両方として解釈できる場合、対応するテンプレートパラメータが非型であっても、常に型IDとして解釈されます。
template<class T> void f(); // #1 template<int I> void f(); // #2 void g() { f<int()>(); // "int()" is both a type and an expression, // calls #1 because it is interpreted as a type }
[edit] 非型テンプレート引数
|
非型テンプレートパラメータで使用できるテンプレート引数は、あらゆる明らかに定数評価される式です。 |
(C++11まで) |
|
非型テンプレートパラメータで使用できるテンプレート引数は、あらゆるイニシャライザ句です。イニシャライザ句が式の場合、それは明らかに定数評価されなければなりません。 |
(C++11以降) |
非型テンプレートパラメータ宣言の型を T とし、パラメータに提供されるテンプレート引数を E とします。
|
考案された宣言 T x = E; は、静的記憶域期間を持つconstexpr 変数の定義に対するセマンティック制約を満たす必要があります。 |
(C++26以降) |
|
推論されたパラメータ型が構造型でない場合、プログラムは不正です。 プレースホルダ型を使用する非型テンプレートパラメータパックの場合、型は各テンプレート引数に対して独立して推論され、一致する必要はありません。 |
(C++17以降) |
template<auto n> struct B { /* ... */ }; B<5> b1; // OK: non-type template parameter type is int B<'a'> b2; // OK: non-type template parameter type is char B<2.5> b3; // error (until C++20): non-type template parameter type cannot be double // C++20 deduced class type placeholder, class template arguments are deduced at the // call site template<std::array arr> void f(); f<std::array<double, 8>{}>(); template<auto...> struct C {}; C<'C', 0, 2L, nullptr> x; // OK
(推論される可能性のある)(C++17以降) 型 T の非型テンプレートパラメータ P の値は、そのテンプレート引数 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; // error: initialization of the template parameter object // is not a constant expression struct J2 { J2 *self = this; constexpr J2() {} constexpr J2(const J2&) {} }; B<J2{}> j2; // error: the template parameter object is not // template-argument-equivalent to introduced temporary
|
非型テンプレートパラメータを持つテンプレートをインスタンス化する場合、以下の制限が適用されます。
特に、これは、文字列リテラル、配列要素のアドレス、および非静的メンバのアドレスは、対応する非型テンプレートパラメータがオブジェクトへのポインタであるテンプレートをインスタンス化するためのテンプレート引数として使用できないことを意味します。 |
(C++17まで) |
|
参照型またはポインタ型の非型テンプレートパラメータ、およびクラス型の非型テンプレートパラメータとそのサブオブジェクト内の参照型またはポインタ型の非静的データメンバ(C++20以降)は、以下を参照したり、以下のアドレスであってはなりません。 |
(C++17以降) |
template<const int* pci> struct X {}; int ai[10]; X<ai> xi; // OK: array to pointer conversion and cv-qualification conversion struct Y {}; template<const Y& b> struct Z {}; Y y; Z<y> z; // OK: no conversion template<int (&pa)[5]> struct W {}; int b[5]; W<b> w; // OK: no conversion void f(char); void f(int); template<void (*pf)(int)> struct A {}; A<&f> a; // OK: overload resolution selects f(int)
template<class T, const char* p> class X {}; X<int, "Studebaker"> x1; // error: string literal as template-argument template<int* p> class X {}; int a[10]; struct S { int m; static int s; } s; X<&a[2]> x3; // error (until C++20): address of array element X<&s.m> x4; // error (until C++20): address of non-static member X<&s.s> x5; // OK: address of static member X<&S::s> x6; // OK: address of static member template<const int& CRI> struct B {}; B<1> b2; // error: temporary would be required for template argument int c = 1; B<c> b1; // OK
[edit] 型テンプレート引数
型テンプレートパラメータのテンプレート引数は、不完全型を命名できる型IDでなければなりません。
template<typename T> class X {}; // class template struct A; // incomplete type typedef struct {} B; // type alias to an unnamed type int main() { X<A> x1; // OK: 'A' names a type X<A*> x2; // OK: 'A*' names a type X<B> x3; // OK: 'B' names a type }
[edit] テンプレートテンプレート引数
テンプレートテンプレートパラメータのテンプレート引数は、クラステンプレートまたはテンプレートエイリアスを命名するid-expressionでなければなりません。
引数がクラステンプレートである場合、パラメータとのマッチングにはプライマリテンプレートのみが考慮されます。部分特殊化がある場合、このテンプレートテンプレートパラメータに基づく特殊化がインスタンス化されるときにのみ考慮されます。
template<typename T> // primary template class A { int x; }; template<typename T> // partial specialization class A<T*> { long x; }; // class template with a template template parameter V template<template<typename> class V> class C { V<int> y; // uses the primary template V<int*> z; // uses the partial specialization }; C<A> c; // c.y.x has type int, c.z.x has type long
テンプレートテンプレート引数 A をテンプレートテンプレートパラメータ P にマッチさせるには、P は A よりも少なくとも特殊化されている必要があります (下記参照)。P のパラメータリストにパラメータパックが含まれている場合、A のテンプレートパラメータリストから0個以上のテンプレートパラメータ (またはパラメータパック) がそれによってマッチされます。(C++11以降)
形式的には、テンプレートテンプレートパラメータ P がテンプレートテンプレート引数 A よりも少なくとも特殊化されているのは、2つの関数テンプレートに対する次の書き換えが与えられたとき、P に対応する関数テンプレートが関数テンプレートの部分順序付け規則に従って A に対応する関数テンプレートよりも少なくとも特殊化されている場合です。テンプレートパラメータリストが A のものと同じである (デフォルト引数を含む) 考案されたクラステンプレート X が与えられたとき、
- 2つの関数テンプレートのそれぞれは、
PまたはAとそれぞれ同じテンプレートパラメータを持ちます。 - 各関数テンプレートは、単一の関数パラメータを持ち、その型は
Xの特殊化であり、それぞれの関数テンプレートのテンプレートパラメータに対応するテンプレート引数を持っています。ここで、関数テンプレートのテンプレートパラメータリスト内の各テンプレートパラメータPPに対して、対応するテンプレート引数AAが形成されます。PPがパラメータパックを宣言する場合、AAはパック展開PP...です。そうでない場合、(C++11以降)AAは id-expressionPPです。
書き換えが無効な型を生成する場合、P は A よりも少なくとも特殊化されていません。
template<typename T> struct eval; // primary template template<template<typename, typename...> class TT, typename T1, typename... Rest> struct eval<TT<T1, Rest...>> {}; // partial specialization of 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: matches partial specialization of eval eval<B<int, float>> eB; // OK: matches partial specialization of eval eval<C<17>> eC; // error: C does not match TT in partial specialization // because TT's first parameter is a // type template parameter, while 17 does not name a type eval<D<int, 17>> eD; // error: D does not match TT in partial specialization // because TT's second parameter is a // type parameter pack, while 17 does not name a type eval<E<int, float>> eE; // error: E does not match TT in partial specialization // because E's third (default) parameter is a non-type
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; // OK after P0522R0 // Error earlier: not an exact match X<C> xc; // OK after P0522R0 // Error earlier: not an exact match template<template<class...> class Q> class Y { /* ... */ }; Y<A> ya; // OK Y<B> yb; // OK Y<C> yc; // OK template<auto n> class D { /* ... */ }; // note: C++17 template<template<int> class R> class Z { /* ... */ }; Z<D> zd; // OK after P0522R0: the template parameter // is more specialized than the template argument template<int> struct SI { /* ... */ }; template<template<auto> class> void FA(); // note: C++17 FA<SI>(); // Error
[edit] デフォルトテンプレート引数
デフォルトテンプレート引数は、パラメータリストで = 記号の後に指定されます。デフォルトはあらゆる種類のテンプレートパラメータ (型、非型、またはテンプレート)、ただしパラメータパックには指定できません(C++11以降) に指定できます。
プライマリクラステンプレート、プライマリ変数テンプレート、(C++14以降) またはエイリアステンプレートのテンプレートパラメータにデフォルトが指定されている場合、後続の各テンプレートパラメータにはデフォルト引数が必要です。ただし、最後の1つはテンプレートパラメータパックである場合があります(C++11以降)。関数テンプレートでは、デフォルトの後に続くパラメータに制限はありません。パラメータパックの後にさらに型パラメータが続くのは、それらにデフォルトがあるか、関数引数から推論できる場合に限られます(C++11以降)。
デフォルトパラメータは許可されていません。
- クラステンプレートのメンバのクラス外定義では (クラス本体内の宣言で提供する必要があります)。非テンプレートクラスのメンバテンプレートは、そのクラス外定義でデフォルトパラメータを使用できることに注意してください (GCCバグ53856を参照)。
- フレンドクラステンプレート宣言では
|
(C++11まで) |
|
フレンド関数テンプレート宣言では、宣言が定義であり、この翻訳単位にこの関数の他の宣言が存在しない場合にのみ、デフォルトテンプレート引数が許可されます。 |
(C++11以降) |
宣言に現れるデフォルトテンプレート引数は、デフォルト関数引数と同様にマージされます。
template<typename T1, typename T2 = int> class A; template<typename T1 = int, typename T2> class A; // the above is the same as the following: template<typename T1 = int, typename T2 = int> class A;
しかし、同じスコープ内で同じパラメータに2回デフォルト引数を指定することはできません。
template<typename T = int> class X; template<typename T = int> class X {}; // error
非型テンプレートパラメータのデフォルトテンプレート引数を解析する場合、最初の非ネストされた > は、より大きい演算子としてではなく、テンプレートパラメータリストの終わりとして解釈されます。
template<int i = 3 > 4> // syntax error class X { /* ... */ }; template<int i = (3 > 4)> // OK class Y { /* ... */ };
テンプレートテンプレートパラメータのテンプレートパラメータリストは、それ自身のデフォルト引数を持つことができ、それらはテンプレートテンプレートパラメータ自体がスコープ内にある場合にのみ有効です。
// class template, with a type template parameter with a default template<typename T = float> struct B {}; // template template parameter T has a parameter list, which // consists of one type template parameter with a default template<template<typename = float> typename T> struct A { void f(); void g(); }; // out-of-body member function template definitions template<template<typename TT> class T> void A<T>::f() { T<> t; // error: TT has no default in scope } template<template<typename TT = char> class T> void A<T>::g() { T<> t; // OK: t is T<char> }
デフォルトテンプレートパラメータで使用される名前のメンバアクセスは、使用時点ではなく、宣言時点でチェックされます。
class B {}; template<typename T> class C { protected: typedef T TT; }; template<typename U, typename V = typename U::TT> class D: public U {}; D<C<B>>* d; // error: C::TT is protected
|
テンプレートが関数を命名するために使用される場合を除き、デフォルトテンプレート引数の値が必要なときに暗黙的にインスタンス化されます。 template<typename T, typename U = int> struct S {}; S<bool>* p; // The default argument for U is instantiated at this point // the type of p is S<bool, int>* |
(C++14以降) |
[edit] テンプレート引数の等価性
テンプレート引数の等価性は、2つのテンプレート識別子が同じであるかどうかを判断するために使用されます。
2つの値がテンプレート引数等価であるとは、それらが同じ型であり、かつ以下のいずれかの条件が満たされる場合です。
- それらが整数型または列挙型であり、その値が同じである。
- それらがポインタ型であり、同じポインタ値を持つ。
- それらがメンバへのポインタ型であり、同じクラスメンバを参照するか、両方ともヌルメンバポインタ値である。
- それらが左辺値参照型であり、同じオブジェクトまたは関数を参照する。
|
(C++11以降) |
|
(C++20以降) |
[edit] 注釈
|
テンプレートパラメータでは、auto の有無によって、型制約が型パラメータと非型パラメータの両方に使用される場合があります。 template<typename> concept C = true; template<C, // type parameter C auto // non-type parameter > struct S{}; S<int, 0> s;
|
(C++20以降) |
| 機能テストマクロ | 値 | 規格 | 機能 |
|---|---|---|---|
__cpp_nontype_template_parameter_auto |
201606L |
(C++17) | auto を使用した非型テンプレートパラメータの宣言 |
__cpp_template_template_args |
201611L |
(c++17) (DR) |
テンプレートテンプレート引数のマッチング |
__cpp_nontype_template_args |
201411L |
(C++17) | すべての非型テンプレート引数に対する定数評価を許可 |
201911L |
(C++20) | 非型テンプレートパラメータにおけるクラス型と浮動小数点型 |
[edit] 例
#include <array> #include <iostream> #include <numeric> // simple non-type template parameter template<int N> struct S { int a[N]; }; template<const char*> struct S2 {}; // complicated non-type example template < char c, // integral type int (&ra)[5], // lvalue reference to object (of array type) int (*pf)(int), // pointer to function int (S<10>::*a)[10] // pointer to member object (of type int[10]) > struct Complicated { // calls the function selected at compile time // and stores the result in the array selected at compile time void foo(char base) { ra[4] = pf(c - base); } }; // S2<"fail"> s2; // error: string literal cannot be used char okay[] = "okay"; // static object with linkage // S2<&okay[0]> s3; // error: array element has no linkage S2<okay> s4; // works int a[5]; int f(int n) { return n; } // C++20: NTTP can be a literal class type template<std::array arr> constexpr auto sum() { return std::accumulate(arr.cbegin(), arr.cend(), 0); } // C++20: class template arguments are deduced at the call site static_assert(sum<std::array<double, 8>{3, 1, 4, 1, 5, 9, 2, 6}>() == 31.0); // C++20: NTTP argument deduction and CTAD static_assert(sum<std::array{2, 7, 1, 8, 2, 8}>() == 28); int main() { S<10> s; // s.a is an array of 10 int s.a[9] = 4; Complicated<'2', a, f, &S<10>::a> c; c.foo('0'); std::cout << s.a[9] << a[4] << '\n'; }
出力
42
| このセクションは未完成です 理由: その他の例 |
[edit] 欠陥報告
以下の動作変更を伴う欠陥報告が、以前に公開されたC++標準に遡って適用されました。
| DR | 適用対象 | 公開された動作 | 正しい動作 |
|---|---|---|---|
| CWG 150 (P0522R0) |
C++98 | テンプレートテンプレート引数は、テンプレートテンプレートパラメータのリストと厳密に一致する必要がありました。 リストと厳密に一致 |
より特殊化された も許可 |
| CWG 184 | C++98 | テンプレートテンプレートパラメータのテンプレートパラメータがデフォルト引数を持つことが許可されているかどうかは未指定 パラメータはデフォルト引数を持つことが許可されているかどうかは未指定 |
仕様が追加されました |
| CWG 354 | C++98 | ヌルポインタ値は非型テンプレート引数にできませんでした | 許可 |
| CWG 1398 | C++11 | 非型テンプレート引数は型 std::nullptr_t を持つことができませんでした |
許可 |
| CWG 1570 | C++98 | 非型テンプレート引数はサブオブジェクトのアドレスを指定できました | 許可されなくなった。 |
| CWG 1922 | C++98 | 名前が注入されたクラス名であるクラステンプレートが、以前の宣言でデフォルト引数を使用できるかどうかは不明でした。 注入されたクラス名 |
許可 |
| CWG 2032 | C++14 | 変数テンプレートの場合、デフォルト引数を持つテンプレートパラメータの後のテンプレートパラメータに制限はありませんでした。 デフォルト引数 |
同じ制限を適用 クラステンプレートの場合と同じ およびエイリアステンプレートの場合と同じ |
| CWG 2542 | C++20 | クロージャ型が構造型であるかどうかは不明でした | 構造型ではない |
| CWG 2845 | C++20 | クロージャ型は構造型ではありませんでした | 構造型である キャプチャがない場合 |
| P2308R1 | C++11 C++20 |
1. 非型テンプレート引数ではリスト初期化が許可されていませんでした (C++11) 非型テンプレート引数 (C++11) 2. クラス型の非型テンプレートパラメータがどのように初期化されるかは不明でした (C++20) パラメータがどのように初期化されるか (C++20) |
1. 許可されました 2. 明確化されました |