読者です 読者をやめる 読者になる 読者になる

C++14 constexpr関数の制限緩和

C++

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


C++14では、constexpr関数の制限がいくつか緩和されます。
緩和されるリストは、以下になります:

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


変数宣言の許可

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

ただし、以下の制限があります:

  • staticやthread_localといった記憶域の指定はできません
  • 未初期化変数の宣言はできません


if文とswitch文の許可

constexpr int abs(int x)
{
    if (x < 0) // OK
        return -x;
    return x;
}
enum class Weekday { Sun, Mon, Tue, };
constexpr Weekday intToWeekday(int n)
{
    switch (n) { // OK
        case 0: return Weekday::Sun;
        case 1: return Weekday::Mon;
        case 2: return Weekday::Tue;
    }
    throw std::out_of_range("n is out of week");
}

ただし、条件分岐としてgoto文は許可されません。


全てのループ文の許可

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

for文、範囲for文、while文、do-while文が許可されます。
さて、returnする値はいくつでしょうか?


変数書き換えの許可

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変数だけなので、グローバル変数のerrnoとかは書き換えられません。


戻り値型としてvoidを許可

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

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

constexprとして扱える型分類である「リテラル型(literal type)」にvoidが追加されます。
これを使用して、参照パラメータを書き換えて返すスタイルが可能になります。


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

struct X {
    constexpr int f();       // C++11では以下と同じ
//  constexpr int f() const;
};

C++11では、constexprメンバ関数は、暗黙にconst修飾されていました。このため、明示的なconst修飾ができませんでした。
C++14ではこの仕様が廃止され、const修飾は明示的に行うことになり、constと非constでオーバーロードが可能になります。