本の虫

著者:江添亮
ブログ: http://cpplover.blogspot.jp/
メール: boostcpp@gmail.com
Twitter: https://twitter.com/EzoeRyou
GitHub: https://github.com/EzoeRyou

アマゾンの江添のほしい物リストを著者に送るとブログ記事のネタになる

筆者にブログのネタになる品物を直接送りたい場合、住所をメールで質問してください。

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