このページはC++20に採用された言語機能の変更を解説しています。
のちのC++規格でさらに変更される場合があるため関連項目を参照してください。
概要
これまで、非型テンプレートパラメータ (non-type template parameter) としては、整数型、ポインタ、参照、std::nullptr_t
、プレースホルダ型の値しか受け取ることが許可されていなかった。
template <int N>
class X;
X<3> x;
C++11以降での汎用的な定数式constexpr
の導入により、多くのクラスオブジェクトを定数として扱えるようになったことを受けて、C++20ではそのように定数として扱える型を広く非型テンプレートパラメータとして受け取ることが許可される。
仕様
非型テンプレートパラメータとして、以下のいずれかに分類される型のオブジェクトを受け取ることができる:
- 構造的型
- プレースホルダ型を含む型 (
template <auto V> class X;
のauto
) - 推論用クラス型のプレースホルダ (
template <class T> A;
があったときのA x{};
のようなクラステンプレートのテンプレート引数推論を意図した型指定)
構造的型 (structural type) とは以下のいずれかに分類される型である:
- スカラ型
- 左辺値参照型
- 以下の特徴をもつリテラルクラス型:
- すべての基本クラスと非静的メンバ変数が
public
かつ非mutable
であり、 - すべての基本クラスと非静的メンバ変数が構造的型もしくはその (多次元) 配列である
- すべての基本クラスと非静的メンバ変数が
備考
- C++20では
std::vector
およびstd::basic_string
を定数式内で使用できるようになるが、これらの型はC++20時点では構造的型に分類されないため、そのオブジェクトをテンプレート引数として渡すことはできない
例
floatやクラス型を非型テンプレートパラメータにする例
#include <utility>
struct X1 {
int i;
float f;
friend bool operator==(const X1&, const X1&) = default;
};
struct X2 {
int i;
float f;
constexpr X2(int i_, float f_)
: i(i_), f(f_) {}
friend bool operator==(const X2&, const X2&) = default;
};
template <int N>
struct C1 {
static constexpr int value = N;
};
template <float N>
struct C2 {
static constexpr float value = N;
};
template <X1 x1, X2 x2>
struct C3 {
static constexpr X1 value1 = x1;
static constexpr X2 value2 = x2;
};
template <auto V>
struct C4 {
static constexpr decltype(V) value = V;
};
int main() {
static_assert(C1<1>::value == 1);
static_assert(C2<3.14f>::value == 3.14f);
constexpr X1 x1{1, 3.14f};
constexpr X2 x2{2, 5.27f};
using c3 = C3<x1, x2>;
static_assert(c3::value1 == x1);
static_assert(c3::value2 == x2);
static_assert(C4<1>::value == 1);
static_assert(C4<3.14f>::value == 3.14f);
static_assert(C4<x1>::value == x1);
static_assert(C4<x2>::value == x2);
constexpr std::pair p{1, 3.14};
static_assert(C4<p>::value == p);
}
59
#include <utility>
struct X1 {
int i;
float f;
friend bool operator==(const X1&, const X1&) = default;
};
struct X2 {
int i;
float f;
constexpr X2(int i_, float f_)
: i(i_), f(f_) {}
friend bool operator==(const X2&, const X2&) = default;
};
出力
プレースホルダーを含む型を非型テンプレートパラメータにする例
#include <iostream>
template <class T>
struct X {
constexpr X(T x) : value(x) {}
T value;
};
template <X x>
struct Y {
void print() {
std::cout << x.value << std::endl;
}
};
int main() {
// Xクラステンプレートのテンプレートパラメータが
// コンストラクタ引数`3`から推論される
Y<3>{}.print();
}
21
#include <iostream>
template <class T>
struct X {
constexpr X(T x) : value(x) {}
T value;
};
template <X x>
struct Y {
void print() {
std::cout << x.value << std::endl;
}
};
int main() {
// Xクラステンプレートのテンプレートパラメータが
// コンストラクタ引数`3`から推論される
出力
3
文字列クラスオブジェクトを非型テンプレートパラメータにしてリテラル演算子を定義する例
#include <iostream>
#include <algorithm>
template <typename CharT, std::size_t N>
struct basic_fixed_string {
constexpr basic_fixed_string(const CharT (&str)[N+1])
{ std::copy_n(str, N+1, data); }
friend auto operator<=>(const basic_fixed_string&, const basic_fixed_string&) = default;
CharT data[N+1];
};
template <typename CharT, std::size_t N>
basic_fixed_string(const CharT (&str)[N]) -> basic_fixed_string<CharT, N-1>;
template <std::size_t N>
using fixed_string = basic_fixed_string<char, N>;
namespace my_literals {
template <basic_fixed_string str>
auto operator""_udl() {
return str;
}
}
int main() {
using namespace my_literals;
auto s = "hello"_udl;
std::cout << s.data << std::endl;
}
31
#include <iostream>
#include <algorithm>
template <typename CharT, std::size_t N>
struct basic_fixed_string {
constexpr basic_fixed_string(const CharT (&str)[N+1])
{ std::copy_n(str, N+1, data); }
friend auto operator<=>(const basic_fixed_string&, const basic_fixed_string&) = default;
CharT data[N+1];
};
template <typename CharT, std::size_t N>
basic_fixed_string(const CharT (&str)[N]) -> basic_fixed_string<CharT, N-1>;
template <std::size_t N>
using fixed_string = basic_fixed_string<char, N>;
出力
hello
備考
- Bug 97930 -
pair
is not a structural type- GCC 10では
pair
が構造的型とみなされないバグがある。GCC 11で修正済み
- GCC 10では