7027


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

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

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

[code]

    #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));
    }
    
[/code]

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

[code]

    $ 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...
    
[/code]

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

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

[code]

    #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);
    }
    
[/code]

逆アセンブル:（該当箇所のみ）

[code]

      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>
    
[/code]

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

[code]

    #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)>::_);
    }
    
[/code]

逆アセンブル:（該当箇所のみ）

[code]

      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>
    
[/code]

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

質問をまとめます。

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

## 追記

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


CC BY-SA 3.0
2015-02-24T06:05:24.853
7012
2015-02-24T10:55:46.250
2015-02-24T10:55:46.250
49
8522
question
c++ c++11
非constexprセンテンス中のconstexprを定数化する方法


テンプレートを使わないという条件であれば、配列と sizeof を使ったインチキが使えます。  
ただし size_t に収まる型に限られる上に、テンプレートを使う方法の劣化版なので無意味です。

[code]

    #include <cstdio>
    
    constexpr int fact(int n) {
        return n <= 1 ? 1 : n * fact(n - 1);
    }
    
    int main() {
        std::printf("fact 10 = %d\n", (int)sizeof(char[fact(10)]));
    }
    
[/code]

テンプレートを使うのであれば、マクロにすることで型の指定を省略することができます。

[code]

    #define AS_CONSTANT(expr) (std::integral_constant<decltype(expr), (expr)>::value)
    
[/code]

いずれにせよ、通常の式中の一部分を定数式評価に強制する方法は、現在の標準では存在しないと思われます。


CC BY-SA 3.0
2015-02-24T10:21:55.780
7027
2015-02-24T10:21:55.780
281
7012
answer