質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

1回答

2534閲覧

constexpr関数の利用方法について

asobinin

総合スコア69

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2019/01/09 12:19

コンパイル時定数を利用するにあたって、テンプレートメタプログラミングの他に、関数の前にconstexprを付けることでinline展開&コンパイル時計算が保証されるということをいくつかのサイトで拝見したことがあります。
そこで実験として、フィナボッチ数列を再帰関数を利用して計算し、その時間を計ってみることにしたのですが、その結果が
-constexpr関数 time[ms]:3713
-テンプレート time[ms]:0
-ノーマル関数 time[ms]:3617 (求める値を40にした場合)
となってしまいました。コンパイル時計算どころか、ノーマル関数より時間が掛かっています・・・

何故こうなったのか、記述が間違っているのか、あるいはそもそもの根本が間違っていたのかお教えください。

C++

1#include <iostream> 2#include <chrono> 3using namespace std; 4 5// フィナボッチ数列(ノーマル) 6unsigned long fina(unsigned long n) { 7 /*if (n <= 2) 8 return 1; 9 return fina(n - 1) + fina(n - 2);*/ 10 11 return (n <= 2) ? 1 : (fina(n - 1) + fina(n - 2)); 12} 13 14// フィナボッチ数列(constexpr) 15constexpr unsigned long finaConst(unsigned long n) { 16 return (n <= 2) ? 1 : (finaConst(n - 1) + finaConst(n - 2)); 17} 18 19// フィナボッチ数列メタプログラミング 20template <unsigned long N> struct Fina { 21 enum { value = Fina<N-1>::value + Fina<N-2>::value }; 22}; 23template <> struct Fina<2> { 24 enum { value = 1 }; 25}; 26template <> struct Fina<1> { 27 enum { value = 1 }; 28}; 29 30// メイン関数 31auto main() ->int { 32 // 関数(constexpr)を利用した場合 33 auto start = chrono::system_clock::now(); 34 cout << finaConst(40) << endl; 35 auto end = chrono::system_clock::now(); 36 cout << "constexpr time[ms]:" << (double)chrono::duration_cast<chrono::milliseconds>(end - start).count() << endl; 37 38 39 // テンプレートを利用した場合 40 auto start2 = chrono::system_clock::now(); 41 cout << Fina<40>::value << endl; 42 auto end2 = chrono::system_clock::now(); 43 cout << "template time[ms]:" << (double)chrono::duration_cast<chrono::milliseconds>(end2 - start2).count() << endl; 44 45 46 // 関数(ノーマル)を利用した場合 47 auto start3 = chrono::system_clock::now(); 48 cout << fina(40) << endl; 49 auto end3 = chrono::system_clock::now(); 50 cout << "nomal time[ms]:" << (double)chrono::duration_cast<chrono::milliseconds>(end3 - start3).count() << endl; 51 52 return 0; 53}

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

定数式の文脈以外ではconstexpr関数がコンパイル時に評価される保証はないからです。

cpp

1constexpr auto re = finaConst(40); 2std::cout << re << std::endl; 3 4#include <type_traits> 5std::cout << std::integral_constant<unsigned long, finaConst(40)>::value << std::endl;

のいずれかのようにする必要があります。


あー、多分わかった。finaConstにわたす回数が大きすぎて再帰上限512を超えているんじゃないかな。それを超えた場合動くかどうかは保証がないので、そういうことでは。40じゃなくて20とかなら通っているので。

そもそもC++14constexrを使って普通にループで書いてあげるのがいいと思います。

投稿2019/01/09 12:50

編集2019/01/09 16:08
yumetodo

総合スコア5850

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

asobinin

2019/01/09 13:22 編集

constexpr auto re = finaConst(40); cout << re << endl; では、「式は定数に評価されませんでした」というエラーが。 integral_constant<unsigned long, finaConst(40)>::value の方法でも、「'_Val': 'std::integral_constant' の無効なテンプレート引数です。コンパイル時の定数式が必要です」というエラーが出てしまいました。 再帰関数自体に問題があるのでしょうか?
yumetodo

2019/01/09 13:34

手元で再現しないのですが、お使いのコンパイラはなんですか?もしVisual Studio 2015 Update 2より前を使っているならバグっているので動かないかも。
asobinin

2019/01/09 13:37

コンパイラの情報も書いておくべきでした・・・ Visual Studio2017 15.9.4を使用しているので、コンパイラが古いというのは考えにくいです
yumetodo

2019/01/09 16:08

追記しました
asobinin

2019/01/09 16:59

yumetodoさんの言う通り、再帰回数を減らしたら無事実行できました。 回数に上限があったなんて知りませんでした・・・ ご助言ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問