constexprを非constexprセンテンス中で使用すると定数になりませんが、簡単な回避方法はないでしょうか。

使用環境: gcc-4.9.2 (MSYS2 32bit), 最適化なし

テストコードを示します。逆アセンブル時の読みやすさのためiostreamではなくprintf()を使っています。

#include <stdio.h>

template <int N> struct Fact    { static const int _ = N * Fact<N - 1>::_; };
template <>      struct Fact<1> { static const int _ = 1; };

constexpr int fact(int n) {
    return n <= 1 ? 1 : n * fact(n - 1);
}

int main() {
    printf("fact 10 = %d\n", Fact<10>::_);
    printf("fact 10 = %d\n", fact(10));
}

main()を逆アセンブルした該当箇所を引用します。

$ g++ -std=c++11 -g fact.cpp
$ objdump -S -Mintel a.exe
...snip...
  printf("fact 10 = %d\n", Fact<10>::_);
4011ce:       c7 44 24 04 00 5f 37    mov    DWORD PTR [esp+0x4],0x375f00
4011d5:       00
4011d6:       c7 04 24 64 30 40 00    mov    DWORD PTR [esp],0x403064
4011dd:       e8 46 00 00 00          call   401228 <_printf>
  printf("fact 10 = %d\n", fact(10));
4011e2:       c7 04 24 0a 00 00 00    mov    DWORD PTR [esp],0xa
4011e9:       e8 b2 05 00 00          call   4017a0 <__Z4facti>
4011ee:       89 44 24 04             mov    DWORD PTR [esp+0x4],eax
4011f2:       c7 04 24 64 30 40 00    mov    DWORD PTR [esp],0x403064
4011f9:       e8 2a 00 00 00          call   401228 <_printf>
...snip...

テンプレートの方はコンパイル時評価で定数化されていますが、constexprは実行時評価です。

一時変数に入れるとコンパイル時評価されますが、同じ定数が二か所で現れてしまいます。(最適化なし)

#include <stdio.h>

constexpr int fact(int n) {
    return n <= 1 ? 1 : n * fact(n - 1);
}

int main() {
    constexpr auto n = fact(10);
    printf("fact 10 = %d\n", n);
}

逆アセンブル:(該当箇所のみ)

  constexpr auto n = fact(10);
4011ce:       c7 44 24 1c 00 5f 37    mov    DWORD PTR [esp+0x1c],0x375f00
4011d5:       00
  printf("fact 10 = %d\n", n);
4011d6:       c7 44 24 04 00 5f 37    mov    DWORD PTR [esp+0x4],0x375f00
4011dd:       00
4011de:       c7 04 24 64 30 40 00    mov    DWORD PTR [esp],0x403064
4011e5:       e8 2a 00 00 00          call   401214 <_printf>

テンプレートで細工すればコンパイル時に評価されます。

#include <stdio.h>

constexpr int fact(int n) {
    return n <= 1 ? 1 : n * fact(n - 1);
}

template <typename T, T N> struct C { static const T _ = N; };

int main() {
    printf("fact 10 = %d\n", C<int, fact(10)>::_);
}

逆アセンブル:(該当箇所のみ)

  printf("fact 10 = %d\n", C<int, fact(10)>::_);
4011ce:       c7 44 24 04 00 5f 37    mov    DWORD PTR [esp+0x4],0x375f00
4011d5:       00
4011d6:       c7 04 24 64 30 40 00    mov    DWORD PTR [esp],0x403064
4011dd:       e8 2a 00 00 00          call   40120c <_printf>

これは結局テンプレートの力を借りているので釈然としません。

質問をまとめます。

  • 前提条件: 最適化なし
  • テンプレートを使わずに、constexprだけで式中の呼び出しをコンパイル時に評価できないでしょうか。
  • もし無理なら、上の例で独自に定義したCのような標準関数はないでしょうか。

追記

テンプレートメタプログラミングをconstexprに置き換える際、前者の感覚で使うとコンパイル時評価にならない落とし穴として、何か定石があるのではないかと期待した質問です。