Namespaces
Variants

Template arguments

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

テンプレートがインスタンス化されるためには、すべての template parameter が対応するテンプレート引数で置き換えられなければなりません。引数は明示的に指定されるか、推論されるか、デフォルト値が使用されます。

template-parameter-list 内のパラメータ( template identifier syntax を参照)は以下のカテゴリのいずれかに属します:

  • 定数テンプレート引数
  • 型テンプレート引数
  • テンプレートテンプレート引数

目次

定数テンプレート引数

別名 非型テンプレート引数 後述 を参照)。

定数テンプレートパラメータで使用できるテンプレート引数は、あらゆる 明示的に定数評価される式 を使用できます。

(C++11まで)

定数テンプレートパラメータで使用できるテンプレート引数は、あらゆる 初期化子節 を使用できます。初期化子節が式である場合、それは 明示的に定数評価される 必要があります。

(C++11以降)

定数テンプレートパラメータ宣言の type T として指定され、そのパラメータに与えられたテンプレート引数が E である場合。

発明された宣言 T x = E ; は、 constexpr 変数 の定義に対する意味的制約を、 静的記憶域期間 で満たさなければならない。

(C++20以降)

T プレースホルダ型 を含む場合、または 推定クラス型のプレースホルダ である場合、テンプレートパラメータの型は、発明された宣言 T x = E ; において変数 x に対して推定された型である。

推定されたパラメータ型が 構造的型 でない場合、プログラムは不適格である。

プレースホルダ型を使用する型を持つ定数テンプレートパラメータパックについては、型は各テンプレート引数に対して独立して推定され、一致する必要はない。

(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 から以下のように決定されます:

  • A が型 T converted constant expression である場合、 P の値は(変換後の) A となる。
  • それ以外の場合、プログラムは不適格となる。
(C++11まで)
  • A が式である場合:
  • A が型 T converted constant expression である場合、 P の値は(変換後の) A となる。
  • それ以外の場合、プログラムは不適格となる。
  • それ以外の場合( A が波括弧で囲まれた初期化子リストである場合)、一時変数 constexpr T v = A ; が導入される。 P の値は v の値となる。
  • v lifetime は初期化直後に終了する。
(C++11から)
(C++20まで)
  • T がクラス型でなく、かつ A が式である場合:
  • A が型 T converted constant expression である場合、 P の値は(変換後の) A となる。
  • それ以外の場合、プログラムは不適格となる。
  • それ以外の場合( T がクラス型であるか、または A が波括弧で囲まれた初期化子リストである場合)、一時変数 constexpr T v = A ; が導入される。
  • v および P の初期化直後に v lifetime が終了する。
  • 以下のいずれかの条件を満たす場合、プログラムは不適格となる:
  • それ以外の場合、 P の値は v の値となる。
(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; // エラー: テンプレートパラメータオブジェクトが
            //        導入された一時オブジェクトとテンプレート引数等価ではありません

定数テンプレートパラメータを持つテンプレートをインスタンス化する際には、以下の制限が適用されます:

  • 整数型および算術型の場合、インスタンス化時に提供されるテンプレート引数は、テンプレートパラメータの型の 変換済み定数式 でなければなりません(したがって、特定の暗黙的変換が適用されます)。
  • オブジェクトへのポインタの場合、テンプレート引数は静的 ストレージ期間 リンケージ (内部または外部)を持つ完全なオブジェクトのアドレス、または適切なnullポインタ値に評価される定数式でなければなりません または std::nullptr_t (C++11以降)
  • 関数へのポインタの場合、有効な引数はリンケージを持つ関数へのポインタ(またはnullポインタ値に評価される定数式)です。
  • 左辺値参照パラメータの場合、インスタンス化時に提供される引数は一時オブジェクト、無名の左辺値、またはリンケージを持たない名前付き左辺値であってはなりません(言い換えれば、引数はリンケージを持たなければなりません)。
  • メンバへのポインタの場合、引数は & Class :: Member として表されるメンバへのポインタ、またはnullポインタ値に評価される定数式でなければなりません または std::nullptr_t (C++11以降)

特に、これは文字列リテラル、配列要素のアドレス、および非静的メンバのアドレスが、対応する定数テンプレートパラメータがオブジェクトへのポインタであるテンプレートをインスタンス化するためのテンプレート引数として使用できないことを意味します。

(C++17まで)

参照型またはポインタ型の定数テンプレートパラメータ およびクラス型の定数テンプレートパラメータとその部分オブジェクトにおける参照型またはポインタ型の非静的データメンバ (C++20以降) は、以下を参照/アドレスとすることはできません:

  • 一時オブジェクト( 参照初期化 中に作成されたものを含む);
  • 文字列リテラル
  • typeid の結果;
  • 事前定義変数 __func__
  • または部分オブジェクト(非静的クラスメンバ、基底部分オブジェクト、または配列要素を含む) 上記のいずれかの (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以降)
  • それらが浮動小数点型であり、その値が同一である場合。
  • それらが配列型である場合(この場合、配列は何らかのクラス/共用体のメンバーオブジェクトでなければならない)、対応する要素がtemplate-argument-equivalentである場合。
  • それらが共用体型であり、両方ともアクティブなメンバーを持たないか、同じアクティブなメンバーを持ち、そのアクティブなメンバーがtemplate-argument-equivalentである場合。
  • それらがラムダクロージャ型である場合。
  • それらが非共用体クラス型であり、対応する直接の部分オブジェクトと参照メンバーがtemplate-argument-equivalentである場合。
(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. 明確化された