最終更新日時:
が更新

履歴 編集

constexprの制限緩和(C++14)

概要

C++11で、汎用定数式の機能であるconstexprが導入された。

constexpr関数には、コードの表現として強い制限があった。C++14では、以下のような制限緩和が行われた。

  • 変数宣言を許可
  • if文とswitch文を許可
  • 全てのループ文を許可(for文、範囲for文、while文、do-while文)
  • 変数の書き換えを許可
  • 戻り値型(リテラル型)として、voidを許可
  • constexpr非静的メンバ関数の、暗黙のconst修飾を削除

仕様

constexpr関数内での変数宣言を許可

以下の例で示すように、constexpr関数内で変数を宣言できるようになった。

constexpr int f()
{
  int result = 0; // OK
                  // 関数f()自体がconstexprであるため、
                  // 変数resultはconstexprである必要はない。
  return result;
}

ただし、

  • staticthread_local記憶域の変数宣言は許可されない。
  • 未初期化変数の宣言は許可されない。

constexpr関数内での条件分岐として、if文とswitch文を許可

以下の例で示すように、constexpr関数内での条件分岐に、if文を使用できるようになった。else節も使用でき、else節を省略してもよい。

constexpr int abs(int x)
{
  if (x < 0) // OK
    return -x;
  return x;
}

また、switch文も使用できるようになった。

enum class Weekday { Sun, Mon, Tue, };
constexpr Weekday intToWeekday(int n)
{
  switch (n) {
    case 0: return Weekday::Sun;
    case 1: return Weekday::Mon;
    case 2: return Weekday::Tue;
    default: break;
  }
  throw std::out_of_range("n is out of week");
}

ただし、goto文は許可されない。

constexpr関数内で、全てのループ文を許可

ループ文として、for文、範囲for文、while文、do-while文が許可された。

constexpr int f()
{
  int x = 0;

  // OK : for文
  for (int i = 0; i < 5; ++i) {
    x += i + 1;
  }

  // OK : 範囲for文
  int ar[] = {6, 7, 8};
  for (const int& i : ar) {
    x += i;
  }

  // OK : while文
  while (true) {
    x += 9;
    break;
  }

  // OK : do-while文
  do {
    x += 10;
  }
  while (false);

  return x;
}

constexpr関数内での、変数の書き換えを許可

constexpr関数内において、ローカル変数、またはその関数が所属するクラスの非静的メンバ変数の書き換えが許可された。

constexpr int square(int x)
{
  x *= x; // OK : ローカル変数は書き換えてもよい
  return x;
}

struct X {
  int x;

  constexpr X(int x)
    : x(x) {}

  constexpr int square()
  {
    x *= x; // OK : 非静的メンバ変数も書き換えられる
    return x;
  }
};

constexpr int square(int n)
{
  X x(n);
  return x.square();
}

constexpr関数の戻り値型として、voidを許可

constexpr関数での、パラメータの型、および戻り値の型は、リテラル型に分類される型に限定される。

C++14では、リテラル型に分類される型に、voidが追加された。

これにより、constexpr関数の戻り値型をvoidとし、非const参照のパラメータを書き換えて結果を返す、という操作が許可された。

constexpr void square(int& x)
{
  x *= x;
}

constexpr int f(int x)
{
  square(x);
  return x;
}

constexpr非静的メンバ関数の、暗黙のconst修飾を削除

C++11では、constexpr非静的メンバ関数は、暗黙でconstが付けられ、明示的にconstを付けることもできなかった:

struct X {
  constexpr int f(); // これは以下と同じ
//constexpr int f() const;
};

C++14ではこの仕様が削除され、constか非constかを、明示的に指定することになった。

※この変更によって、既存コードの互換性は壊れない。

この機能が必要になった背景・経緯

C++は直交性を重視して設計されており、直接関係ない機能同士を組み合わせて使用できる。しかし、C++11でのconstexprは、その制限によって、ほかの機能(インスタンス、forループ、変数書き換え、例外等)とうまく組み合わせられなかった。

これらの制限を回避するために表現力を犠牲にしなければならず、プログラマをいらつかせていた。

C++14では、constexpr関数、constexprメンバ関数、暗黙のconstといった制限を緩和する。

関連項目

参照