最終更新日時: 2026年05月03日 20時37分18秒 (JST)
Koichi Murase が更新

履歴 編集

# `constexpr`関数内での条件分岐とループの文を許可 [N3652](C++14)

このページはC++14に採用された言語機能の変更を解説しています。

のちのC++規格でさらに変更される場合があるため関連項目を参照してください。

## 概要

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

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

- 変数宣言を許可
- `if`文と`switch`文を許可
- 全てのループ文を許可(`for`文、範囲`for`文、`while`文、`do-while`文)
- 変数の書き換えを許可
- 戻り値型(リテラル型)として、`void`を許可 / 戻り値型や関数引数で非`const`参照を許可
- `constexpr`非静的メンバ関数の、暗黙の`const`修飾を削除
- `constexpr`コンストラクタがbodyを持てるようになった

## 仕様

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

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

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

ただし、

- `static`／`thread_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`を許可 / 戻り値型や関数引数で非`const`参照を許可

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

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

また、戻り値型や関数引数で非`const`参照を使うことが許可された。

これにより、`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`かを、明示的に指定することになった。

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

### `constexpr`コンストラクタがbodyを持てるようになった

C++11では、`constexpr`コンストラクタのbodyには以下の要素しか持たせることを許されていなかった：

- 空文
- `static_assert`
- クラスや列挙型を定義しない、別の型名定義
- `using`宣言と、`using`ディレクティブ

これは事実上`constexpr`コンストラクタのbodyが空でなければいけないことを意味している。

C++14では`constexpr`コンストラクタのbodyに関する制約は一般の`constexpr`関数に従うようになったため、body内でローカル変数を定義したり引数に応じたメンバ変数の書き換えを行ったりすることが許可された。

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

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

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

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

## 関連項目

- C++11 `constexpr`

## 参照

- N3597 Relaxing constraints on `constexpr` functions
- N3598 `constexpr` member functions and implicit `const`
- N3652 Relaxing constraints on `constexpr` functions