このページはC++17に採用された言語機能の変更を解説しています。
のちのC++規格でさらに変更される場合があるため関連項目を参照してください。
概要
C++14まで、以下のように書いていた「指定された型の定数を受け取る」意図の非型テンプレートパラメータ(non-type template parameter)だが、
template <class T, T V>
struct X;
X<int, 3>;
C++17ではこの用途のためのシンタックスシュガー(糖衣構文、syntactic sugar)が導入され、テンプレートパラメータをauto
にして値を受け取ることができるようになった。
template <auto X>
struct A {};
A<3>; // OK
A<true>; // OK
A<'a'>; // OK
A<3.14>; // コンパイルエラー (浮動小数点数は渡せない)
テンプレートの中では、decltype
を使用すればX
の型を取得できる。
このauto
は、変数宣言のauto
と同じくプレースホルダーという扱いになる。そのため、template <auto* P>
やtemplate <auto& R>
のような推論補助もできる。また、auto
の代わりにdecltype(auto)
を使うこともできる。
仕様
n4659 [temp.param]/4より
型ではないテンプレートパラメータは、次の型(cv 修飾されていてもかまわない)のうちの1つを持たなければならない:
- 整数型または列挙型
- オブジェクトへのポインタまたは関数へのポインタ
- オブジェクトへの左辺値参照または関数への左辺値参照
- メンバへのポインタ
std::nullptr_t
- プレースホルダ型を含む型 <-- この行が追加された
なお、[temp.param]/4 は C++20 で変更予定である。Working Draft, Standardを参照。
この他にも変更点が多くある。P0127R2 Declaring non-type template arguments with autoを参照。
例
#include <type_traits>
template <auto* X>
struct A {
using type = decltype(X);
};
int main()
{
constexpr int* p = nullptr;
static_assert(std::is_same<A<p>::type, int*>{});
}
出力
この機能が必要になった背景・経緯
以下、P0127R1 Declaring non-type template arguments with autoより引用した。
この提案では、auto
プレースホルダ型指定子を使用して型でないテンプレートパラメータを宣言できるようにすることを提案する。望ましい効果は、同様の構文が総称ラムダに対して機能するのと同じように、対応する型ではないテンプレート引数の型が自動的に推定されることである。
現在、型ではないテンプレートパラメータの型を明示的に指定しなければならない。これは、任意の型の定数引数を取ることを意図したテンプレートを書くときに不必要な冗長性と柔軟性の低下を招く。
template <typename T, T v> struct S { }; // 定義
S<decltype(x), x> s; // インスタンス化
この例では、decltype
を使用して、x
の型と値の両方をS
に渡す前に、x
の型(コンパイル時定数)を取得する。目的は、x
の型を別のテンプレート引数として渡す必要がないようにS
の宣言を変更できるようにすることである。これにより、次のようなより簡単なインスタンス化が可能になる。
S<x> s; // desired instantiation
これは、テンプレートパラメータリストでauto
キーワードを使用できるようにすることで実現できる。
template <auto v> struct S; // type of v is deduced