C++11:定数式の一般化とその保障:constexpr
C++11では、定数式を定義できるようになった。
enum class Flag: unsigned int { good = 1, fail = 2, bad = 4, eof = 8 }; constexpr Flag operator|(Flag x, Flag y) { return static_cast<Flag>( static_cast<unsigned int>(x) | static_cast<unsigned int>(y)); } void func(Flag f) { switch ( f ) { case Flag::bad: /* ... */ break; case Flag::eof: /* ... */ break; case Flag::bad|Flag::eof: /* ... */ break; default: /* ... */ break; } }
定数式には、コンパイル時に不明な値は利用できない。
int x1 = 7; constexpr int x2 = 7; constexpr int x3 = x1; // エラー:初期化子が定数式でない constexpr int x4 = x2; // OK
constexpr関数は、単純でなければならない。具体的には、return文は一つだけ許され、局所変数や選択文(if文など)や繰り返し文(for文など)は認められない。また、副作用を持ってもいけない。
ただし、条件式と再帰は使えるので、本当に必要であればあらゆるものをconstexpr関数にできる。(これらの制限の一部は、C++14で緩和された。)
constexpr int fact(int n) { return (n > 1) ? n * fact(n - 1) : 1; }
関数定義におけるconstexprは、コンパイル時に評価できることを示し、オブジェクト定義におけるconstexprは、初期化子をコンパイル時に評価する必要があることを示す。
なお、constexpr関数は、非定数の実引数を与えて呼び出すこともできるので、非定数用に同じ内容の関数を定義する必要はない。
void func(int n) { int f5 = fact(5); // コンパイル時に評価される可能性がある int fn = fact(n); // 実行時に評価される constexpr int f6 = fact(6); // コンパイル時に評価される constexpr int fnn = fact(n); // エラー:コンパイル時に評価できない }
constexprは、クラスのコンストラクターに付けることもできるが、コンストラクターの本体は空でなければならず、メンバー初期化子並びかオブジェクトの初期化子で全てのメンバー変数を初期化しなければならない。
struct Point { int x, y; constexpr Point(int xx = 0, int yy = 0): x{xx}, y{yy} {} }; constexpr Point origin; constexpr int ox{origin.x}; constexpr Point points[]{Point{1,2}, Point{3,4}, Point{5,6}}; constexpr int x{points[1].x};
constexprコンストラクターを持つクラスは、リテラル型と呼ばれる。
constexprは、全てのconstを置き換えるものではないことに注意しよう。
参考
- constant expressions (generalized and guaranteed;constexpr) (定数式 (一般化と保証付け; constexpr))
- C++14 constexpr関数の制限緩和 - Faith and Brave - C++で遊ぼう
- 作者: ビャーネ・ストラウストラップ,Bjarne Stroustrup,柴田望洋
- 出版社/メーカー: SBクリエイティブ
- 発売日: 2015/02/28
- メディア: 大型本
- この商品を含むブログ (8件) を見る