テンプレート仮引数とテンプレート実引数
すべてのテンプレートは、テンプレート宣言の構文の parameter-list で示される、1個以上のテンプレート仮引数によって、パラメータ化されます。
template < parameter-list > declaration
|
(1) | ||||||||
parameter-list 内のそれぞれの仮引数は、以下のいずれかです。
- 非型テンプレート仮引数
- 型テンプレート仮引数
- テンプレートテンプレート仮引数
目次 |
[編集] 非型テンプレート仮引数
type name(オプション) | (1) | ||||||||
type name(オプション) = default
|
(2) | ||||||||
type ... name(オプション)
|
(3) | (C++11以上) | |||||||
placeholder name | (4) | (C++17以上) | |||||||
auto
(単なる auto
や、 auto **、 auto & など)、推定されたクラス型のためのプレースホルダ (C++20以上)、 decltype(auto) を含む、任意の型であって構いません。type は以下の型のいずれかです (cv 修飾されていても構いません。 修飾子は無視されます)。
- 左辺値参照型 (オブジェクトへの、または関数への)
|
(C++20未満) |
const T 型の glvalue
|
(C++20以上) |
配列および関数型をテンプレート宣言に書いても構いませんが、それらは適切なオブジェクトへのポインタまたは関数へのポインタに自動的に置き換えられます。
非型テンプレート仮引数の名前がクラステンプレート本体内の式で使用されたとき、その型が左辺値参照型またはクラス型 (C++20以上)でない限り、それは変更不可能な prvalue です。
たとえ他の場所では class Foo が複雑型指定子であって class Foo x; が Foo
型の x
を宣言するのであっても、 class Foo 形式のテンプレート仮引数は名前を持たない Foo
型の非型テンプレート仮引数ではありません。
非型テンプレート仮引数の型は、プレースホルダ型 ( template<auto n> struct B { /* ... */ }; B<5> b1; // OK、非型テンプレート仮引数の型は int です。 B<'a'> b2; // OK、非型テンプレート仮引数の型は char です。 B<2.5> b3; // エラー、非型テンプレート仮引数の型は double にできません。 プレースホルダ型を使用する非型テンプレートパラメータパックの場合は、型はテンプレート実引数のそれぞれについて個別に推定され、一致する必要はありません。 template<auto...> struct C {}; C<'C', 0, 2L, nullptr> x; // OK |
(C++17以上) |
クラス型 struct A { friend bool operator==(const A&, const A&) = default; }; template<A a> void f() { &a; // OK。 const A& ra = a, &rb = a; // どちらも同じテンプレート仮引数オブジェクトに束縛されます。 assert(&ra == &rb); // パスします。 } |
(C++20以上) |
[編集] 型テンプレート仮引数
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-constraint | - | コンセプトの名前、またはコンセプトの名前の後に (山括弧で囲った) テンプレート実引数のリストが続いたものの、いずれか。 いずれの場合でも、コンセプトの名前は修飾名でも構いません。 |
type-parameter-key は typename
と class
のいずれかです。 型テンプレート仮引数の宣言において、これらのキーワードに違いはありません。
template<class T> class My_vector { /* ... */ };
template<class T = void> struct My_op_functor { /* ... */ };
仮引数の名前はオプショナルです。
// 型テンプレート仮引数を持つテンプレートの宣言。 template<class> class My_vector; template<class = void> struct My_op_functor; template<typename...> class My_tuple;
テンプレート宣言の本体の中では、型引数の名前は typedef 名であり、そのテンプレートが実体化されたときに供給された型のエイリアスとなります。
type-constraint がコンセプト
template<typename T> concept C1 = true; template<typename... Ts> concept C2 = true; // 可変長引数コンセプト template<typename T, typename U> concept C3 = true; template<C1 T> struct s1; // 制約式は C1<T> です。 template<C1... T> struct s2; // 制約式は (C1<T> && ...) です。 template<C2... T> struct s3; // 制約式は (C2<T> && ...) です。 template<C3<int> T> struct s4; // 制約式は C3<T, int> です。 template<C3<int>... T> struct s5; // 制約式は (C3<T, int> && ...) です。 |
(C++20以上) |
[編集] テンプレートテンプレート仮引数
template < parameter-list > typename(C++17)|class name(オプション)
|
(1) | ||||||||
template < parameter-list > typename(C++17)|class name(オプション) = default
|
(2) | ||||||||
template < parameter-list > typename(C++17)|class ... name(オプション)
|
(3) | (C++11以上) | |||||||
型テンプレート仮引数の宣言と異なり、テンプレートテンプレート仮引数の宣言はキーワード |
(C++17未満) |
テンプレート宣言の本体の中では、この仮引数の名前はテンプレート名です (実体化するためには実引数が必要です)。
template<typename T> class my_array {}; // 2個の型テンプレート仮引数と1個のテンプレートテンプレート仮引数。 template<typename K, typename V, template<typename> typename C = my_array> class Map { C<K> key; C<V> value; };
[編集] テンプレート実引数
テンプレートを実体化するためには、すべてのテンプレート仮引数 (型、非型、またはテンプレート) を対応するテンプレート実引数で置き換えなければなりません。 クラステンプレートの場合、実引数は、明示的に提供する、初期化子から推定する (C++17以上)、デフォルト値を用いる、のいずれかです。 関数テンプレートの場合、実引数は、明示的に提供する、文脈から推定する、デフォルト値を用いる、のいずれかです。
実引数が型識別子と式のいずれにも解釈できる場合は、たとえ対応するテンプレート仮引数が非型であっても、常に型識別子として解釈されます。
template<class T> void f(); // #1 template<int I> void f(); // #2 void g() { f<int()>(); // 「int()」は、型と式のどちらでもあります。 // これは、型として解釈されるため、 #1 を呼びます。 }
[編集] 非型テンプレート実引数
非型テンプレート仮引数を持つテンプレートを実体化するときは以下の制限が適用されます。
特に、これは、文字列リテラル、配列の要素のアドレス、非静的メンバのアドレスを、対応する非型テンプレート仮引数がオブジェクトへのポインタであるテンプレートを実体化するためのテンプレート実引数としては使用できない、ということを暗に示します。 |
(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) が選択されます。 唯一の例外は、参照またはポインタ型の非型テンプレート仮引数およびクラス型の非型テンプレート仮引数における参照またはポインタ型の非静的データメンバおよびその部分オブジェクト (C++20以上)は、以下のアドレスを表す/参照することはできないということです。
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; // エラー、配列要素のアドレス。 X<&s.m> x4; // エラー、非静的メンバのアドレス。 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。 |
(C++17以上) |
[編集] 型テンプレート実引数
型テンプレート仮引数に対するテンプレート実引数は、型識別子でなければなりません。 これは不完全型を表していても構いません。
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」は型を表します。 }
[編集] テンプレートテンプレート実引数
テンプレートテンプレート仮引数に対するテンプレート実引数は、クラステンプレートまたはテンプレートエイリアスを表す識別子式でなければなりません。
実引数がクラステンプレートの場合は、仮引数をマッチングするとき、プライマリテンプレートのみが考慮されます。 部分特殊化 (もしあれば) は、そのテンプレートテンプレート仮引数に基づいた特殊化が実体化されることになったときにのみ、考慮されます。
template<typename T> class A { int x; }; // プライマリテンプレート。 template<class 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
にマッチするためには、 A
のテンプレート仮引数のそれぞれが P
の対応するテンプレート仮引数に正確にマッチしなければなりません (C++17未満) P
は少なくとも A
と同程度に特殊化されていなければなりません (C++17以上)。 P
の仮引数リストがパラメータパックを含む場合は、 A
のテンプレート仮引数リストから0個以上のテンプレート仮引数 (またはパラメータパック) がそれにマッチされます。
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; // エラー、 TT の第1仮引数が型テンプレート仮引数であるのに対して、 // 17 は型を表さないため、 // C は部分特殊化の TT にマッチしません。 eval<D<int, 17>> eD; // エラー、 TT の第2仮引数が型パラメータパックであるのに対して、 // 17 は型を表さないため、 // D は部分特殊化の TT にマッチしません。 eval<E<int, float>> eE; // エラー、 E の第3仮引数 (デフォルト値を使用) は非型であるため、 // E は部分特殊化の TT にマッチしません。
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; // CWG 150 適用後の C++17 では OK // それより前ではエラー (正確なマッチでない) X<C> xc; // CWG 150 適用後の C++17 では 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; // OK template <int> struct SI { /* ... */ }; template <template <auto> class> void FA(); // 注: C++17 FA<SI>(); // エラー
形式的には、以下の2つの関数テンプレートへの書き換えが与えられたとき、関数テンプレートのための半順序のルールに従って、
書き換えが無効な型を生成する場合は、 |
(C++17以上) |
[編集] デフォルトテンプレート引数
デフォルトテンプレート引数は、仮引数リスト内で = 記号の後に指定されます。 デフォルトはいかなる種類のテンプレート引数 (型、非型、テンプレート) に対しても指定できますが、パラメータパックにはできません。
プライマリクラステンプレート、プライマリ変数テンプレート (C++14以上)またはエイリアステンプレートに対してデフォルトが指定されている場合は、そのテンプレート引数より後のテンプレート引数はすべてデフォルト引数を持たなければなりません。 ただし、最後の引数はパラメータパックでも構いません。 関数テンプレートでは、デフォルトの後の引数に対する制限はありません。 また、デフォルトを持つか実引数から推定できる場合に限り、パラメータパックの後に型引数があっても構いません。
デフォルト引数は以下の場所では使用できません。
- クラステンプレートのメンバのクラスの外側の定義において (デフォルトはクラス本体の内側の宣言で提供しなければなりません)。 非テンプレートクラスのメンバテンプレートはクラスの外側の定義でデフォルト引数を使用できることに注意してください (GCC bug 53856 を参照)。
- フレンドクラステンプレートの宣言において。
|
(C++11未満) |
フレンド関数テンプレートの宣言では、デフォルトテンプレート引数は、その宣言が定義であり、その関数の他の宣言がその翻訳単位に現れない場合にのみ許されます。 |
(C++11以上) |
宣言に現れるデフォルトテンプレート引数と定義に現れるデフォルトテンプレート引数は、デフォルト関数引数と同様にマージされます。
template<typename T1, typename T2 = int> class A; template<typename T1 = int, typename T2> class A; // 上記は以下と同じです。 template<typename T1 = int, typename T2 = int> class A;
しかし、同じスコープで同じ引数にデフォルト引数を複数回与えることはできません。
template<typename T = int> class X; template<typename T = int> class X {}; // エラー。
テンプレートテンプレート仮引数のテンプレート仮引数リストは、それ自身のデフォルト引数を持つことができます。 これはそのテンプレートテンプレート仮引数自身がスコープ内である場合にのみ効果があります。
// デフォルト付きの型テンプレート仮引数を持つクラステンプレート。 template<typename T = float> struct B {}; // テンプレートテンプレート仮引数 T は、デフォルト付きの // 型テンプレート仮引数1個から構成される、仮引数リストを持ちます。 template<template<typename = float> typename T> struct A { void f(); void g(); }; // 本体の外側のメンバ関数テンプレートの定義。 template<template<typename TT> class T> void A<T>::f() { T<> t; // エラー、 TT はスコープ内ではデフォルトを持ちません。 } template<template<typename TT = char> class T> void A<T>::g() { T<> t; // OK、 t は 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; // エラー、 C::TT は protected です。
デフォルトテンプレート引数は、そのテンプレートが関数を表すために使用される場合を除いて、そのデフォルト引数の値が必要になったときに、暗黙に実体化されます。 template<typename T, typename U = int> struct S { }; S<bool>* p; // U のデフォルト引数はここで実体化されます。 // p の型は S<bool, int>* です。 |
(C++14以上) |
[編集] 例
[編集] 非型テンプレート引数
#include <iostream> // 単純な非型テンプレート引数。 template<int N> struct S { int a[N]; }; template<const char*> struct S2 {}; // 複雑な非型の例。 template < char c, // 整数型 int (&ra)[5], // (配列型の) オブジェクトへの左辺値参照 int (*pf)(int), // 関数へのポインタ int (S<10>::*a)[10] // (int[10] 型の) メンバオブジェクトへのポインタ > struct Complicated { // コンパイル時に選択された関数を呼び、その結果を // コンパイル時に選択された配列に格納します。 void foo(char base) { ra[4] = pf(c - base); } }; S2<"fail"> s2; // エラー、文字列リテラルは使用できません。 char okay[] = "okay"; // リンケージを持つ静的オブジェクト。 S2< &okay[0] > s2; // エラー、配列の要素はリンケージを持ちません。 S2<okay> s2; // 動作します。 int a[5]; int f(int n) { return n; } int main() { S<10> s; // s.a は int 10個の配列です。 s.a[9] = 4; Complicated<'2', a, f, &S<10>::a> c; c.foo('0'); std::cout << s.a[9] << a[4] << '\n'; }
出力:
42
This section is incomplete Reason: more examples |