このページは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
といった制限を緩和する。