モノトーンの伝説日記

Apex Legends, Splatoon, Programming, and so on...

C++ (fork) Advent Calendar 2013 4 日目 ~constexpr with VC++~

 こちらの記事は「C++ (fork) Advent Calendar 2013」の記事になります。


 自転車操業で続いている C++ (fork) Advent Calendar 2013。いつまで続くのでしょう…w

 Visual C++ Compiler November 2013 CTP*1 から C++ の機能実装が追加され、constexpr (メンバー関数を除く) を利用できるようになりました。VC++er な私は、constexpr に触れたことが全然ないので、今回は constexpr で遊んでみる記事を書いてみたいと思います (それ故クオリティーが低いです)。それでは、始めていきましょう。

概要

  1. constexpr とは何ぞや?
  2. constexpr はどんなことができるのか?
  3. まとめ

1. constexpr とは何ぞや?

 英語版 wikipedia にこんなわかりやすい一文がありました。

C++ has always had the concept of constant expressions. These are expressions such as 3+4 that will always yield the same results, at compile time and at run time. Constant expressions are optimization opportunities for compilers, and compilers frequently execute them at compile time and hardcode the results in the program.*2

なるほど… まあこんな感じです。

C++ は定数式の概念を持っています。 3+4 のような式はコンパイル時だろうが実行時だろうが同じ結果を得られるだろう。定数式はコンパイラーに最適化のための機会を与え、コンパイラーはコンパイル時にそれらを実行し、プログラムの結果をハードコードする。
(ワタシが訳しました、誤解を招く表現があれば指摘していただけるとありがたい。)

まあつまり、「実行時」でも「コンパイル時」でも変わらない不変な値、例えばテイラー展開の結果などをあらかじめ計算しておこう! ということです。

 VC++ は Ox 最適化をかけるとある程度のものは展開されますが、もちろん展開されないものもあります。そのとき、コンパイラーが“最適化する指標”として私たちが情報をちょっと与えてやるという概念になります。

 現状、VC++ の constexpr はメンバー関数に適応できませんが、メンバー関数に適応できなくても、Ox では展開されることが多いと思いますのであまり問題にしていません。ただ、for や if などは使えませんから、条件演算子や再帰を駆使しての記述となりますので、自由度は低いですが…

 それなりにできることについて書いていきたいと思います。

2. constexpr はどんなことができるのか?

 VC++ でできることをちょっと書いてみます。

2.1. コンパイル時べき乗

constexpr double pow( double x, size_t y )
{
    return y != 1 ? x * pow( x, y - 1 ) : x;
}

int wmain( int argc, wchar_t* argv )
{
    double a = pow( 2.0, 2 );
    double b = pow( 3.0, 6 );
    double c = pow( 2.0, 512 ); // 513 回にすると警告が出る。デフォルトは 512 回までの設定になっている

    return 0;
}

 It’s very easy! Yeah! (なんで英語やねん)

2.2. コンパイル時階乗 (0! = 1 と定義)

 拡張階乗みたいな 0.5! とかそういうのもありますが、よく公式とかに使われるタイプの階乗 (0! = 1) のほうを実装します。負の数が来た場合は NaN とします。

constexpr double fact_impl( double v )
{
    return v != 2.0 ? v * fact_impl( v - 1.0 ) : v;
}

constexpr double factorial( double v )
{
    return v == 0.0 ? 1.0 : v > 0.0 ? fact_impl( v ) : std::numeric_limits<double>::quiet_NaN();
}

int wmain( int argc, wchar_t* argv )
{
    double a = factorial( 5.0 );
    double b = factorial( 40.0 );
    double c = factorial( -5.0 );
    double d = factorial( 0.0 );

    return 0;
}

  コンパイル時階乗も 2 つの関数を使えばこんな風に書けます。

3. まとめ

 これぐらいしか思いつきませんでしたー! メンバー関数には constexpr が使えないので、あまり使い道ないと思います、ハイ。でも VC++ 班に少しでも constexpr を実装する気があったんだ、っていう事実はわかったので、あとは時間が解決する問題だと思うので、首を長くして待てばいいのではないのでしょうか?

 おわり。