最終更新日時(UTC):
が更新

履歴 編集

非型テンプレートパラメータとしてクラス型を許可する [P0732R2]

このページは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であり、
    • すべての基本クラスと非静的メンバ変数が構造的型もしくはその (多次元) 配列である

備考

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);
}

出力

プレースホルダーを含む型を非型テンプレートパラメータにする例

#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();
}

出力

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;
}

出力

hello

備考

関連項目

参照