constexprで非定数式の状態を保持
Non-constant constant-expressions in C++
なんと、C++のconstexpr関数を呼び出すたびに戻り値を変える方法があるという。つまり、以下のstatic_assertが引っかかるコードだ。
int main ()
{
constexpr int a = f ();
constexpr int b = f ();
static_assert (a != b, "fail");
}
なんと、constexpr関数は状態を保持できる計算能力を備えているというのだ。
fはconstexpr関数である。
読んでみたところ、要するにこうだ。
noexcept演算子はオペランドが定数式かどうかを判別するのに使える。
// exprが定数式であればtrue
constexpr bool b = noexcept( expr ) ;
未定義の関数は定数式ではない。
// 宣言
constexpr int f( int ) ;
void check1( )
{
noexcept( f( 0 ) ) ; // false
}
// 定義
constexpr int f( int ) { return 0 ; }
void check2( )
{
noexcept( f(0) ) ; // true
}
friend宣言は関数の定義を書くことができる。
constexpr int f( int ) ;
struct S
{
friend constexpr int f( int ) { return 0 ; }
} ;
friend宣言で定義した関数はADL経由でしか呼び出せないが、それは問題ではない。friend宣言が現れて初めてconstexpr関数fが定義され、呼び出しが定数式になるということだ。
もし、frined宣言をするクラスをテンプレートにしたらどうなるだろうか。テンプレートが実体化した時だけ、関数fは定義されるということになる。
// 定義されたかどうかの1bitのフラグ
constexpr int flag (int);
// 関数を定義することでフラグ書き込む
template<class Tag>
struct writer {
friend constexpr int flag (Tag) {
return 0;
}
};
// writerの実体化を遅延させるためのラッパー
template<bool B, class Tag = int>
struct dependent_writer : writer<Tag> { };
// 実際の使い方
// 一回目に呼ばれた時点ではまだwriterが実体化しておらず、
// flag<int>は定義されていない
// 二度目以降に定義されるのでtrueとなる
template<
bool B = noexcept (flag (0)),
int = sizeof (dependent_writer<B>)
>
constexpr int f () {
return B;
}
int main () {
constexpr int a = f ();
constexpr int b = f ();
static_assert (a != b, "fail");
}
あとはこれを並べれば、何ビットでも状態が保持できる。
ドワンゴ広告
GWは長い。今週は休みだが、有給を申請し忘れたので木金は出社する。
ドワンゴは本物のC++プログラマーを募集しているようですが、休暇中なのでa要素を使ってリンクしません。
<a href="http://info.dwango.co.jp/recruit/">採用情報|株式会社ドワンゴ</a>
CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0