非constexprセンテンス中のconstexprを定数化する方法
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
に置き換える際、前者の感覚で使うとコンパイル時評価にならない落とし穴として、何か定石があるのではないかと期待した質問です。