2013-12-23 15 views
8

私は次のようconstexprの機能を定義していますC++のconstexpr関数は実際に非定数式を引数として受け入れることができますか?

constexpr int foo(int i) 
{ 
    return i*2; 
} 

そして、これは主な機能には何です:

int main() 
{ 
    int i = 2; 
    cout << foo(i) << endl; 
    int arr[foo(i)]; 
    for (int j = 0; j < foo(i); j++) 
     arr[j] = j; 
    for (int j = 0; j < foo(i); j++) 
     cout << arr[j] << " "; 
    cout << endl; 
    return 0; 
} 

プログラムは、コマンドの打ち鳴らすの++とOS X 10.8でコンパイルされました。コンパイラがfoo(i)の定数式ではないというエラーメッセージを出さず、実際にコンパイルされたプログラムがうまく動作することに驚きました。どうして?

+2

'constexpr'手段」は定数を生成することができます式は、 "not"は定数式を生成する必要があります。 – Angew

+0

@Angew配列次元はconstexprでなければいけませんか? –

+2

@ God_of_Thunderはコンパイラの拡張機能です – Simple

答えて

21

C++でのconstexpr関数の定義は、定数式のみが評価に使用されるように呼び出されたときに定数式を生成できることが保証されているようなものです。評価がコンパイル時または実行時に行われるかどうかは、結果がconstexprで使用されていない場合は指定されません(this answerも参照)。定数でない式をconstexprに渡すと、定数式が得られないことがあります。

iは結果を生成するためにfoo()によって明示的に使用される定数式ではないため、上記のコードはコンパイルしないでください。この結果、配列次元として使用されます。

warning: variable length arrays are a C99 feature [-Wvla-extension] 

何かがあるかどうかを確認するために、より良いテスト、確かに、定数式は、の値を初期化するためにそれを使用することです:私のために次の警告を生成してclangはCスタイルの可変長配列を実装しているようですconstexpr、例えば:

constexpr int j = foo(i); 
+0

コンパイル中に警告メッセージが表示されませんでした。コンパイラの違いは? –

+1

@ God_of_Thunder:おそらく私の多少の肛門コンパイラフラグ: 'clang ++ -stdlib = libC++ -nostdinC++ -std = C++ 11 -O2 -pedantic -W -Wall -c test.cpp'しかし、実行する前に多くのエラーをキャプチャします。 –

+1

私のプログラムに上記のconstexprの定義を追加しようとしました。私の古いclang ++コマンドでコンパイルしようとしました。 "非定数変数 'i'は定数式では許されません"というエラーメッセージが生成されました。だから、コンパイラ拡張は配列の次元だけで普通のconstexprの定義では動かないのですか? –

0

これは古い質問ですが、それは「非定数であるconstexprの関数の戻り」VSエラーメッセージのためのGoogle検索での最初の結果です。私の状況を助けるものではありませんが、私は私の2セントを置くと思っていました...

Dietmarはconstexprについてよく説明していますが、エラーは直ちにキャッチする必要があります-pedanticフラグ) - このコードは、コンパイラの最適化に苦しんでいるようです。

値iは2に設定されていますが、プログラムの期間は変更されません。コンパイラはおそらくこのことに気付き、変数を定数に最適化しました(関数にそのパラメータを適用する前に、変数iへのすべての参照を定数2に置き換える)ので、foo()へのconstexpr呼び出しを作成します。

私はfoo(i)の呼び出しが定数値4に置き換えられたのを見ると分かりますが、これはプログラムの実行中にこの関数を呼び出すための唯一の可能な戻り値だからです。

-pedanticフラグを使用すると、コンパイラは厳密な観点からプログラムを解析します(おそらく最適化の前に実行されます)。その結果、エラーがキャッチされます。

+0

'-pedantic'は最適化レベルを変更しません。非標準の拡張機能を無効にします。 Dietmarの説明は私にとって良いようですが、あなたのポイントを証明するアセンブリを作ることができれば、より良いケースが得られます! –

1

「g ++ -std = C++ 11 code.cc」を使用してコンパイルすると、先頭に「using namespace std;」が追加され、エラーは発生しませんでした(下記参照)このコード)ここでは、コードと出力されます:「... constexprの機能は、その返り値コードを消費することは、それを必要とするとき、コンパイル時に計算することができるものであるconstexprの機能:。

#include <iostream> 
using namespace std; 

constexpr int foo(int i) 
{ 
    return i*2; 
} 

int main() 
{ 
    int i = 2; 
    cout << foo(i) << endl; 
    int arr[foo(i)]; 
    for (int j = 0; j < foo(i); j++) 
     arr[j] = j; 
    for (int j = 0; j < foo(i); j++) 
     cout << arr[j] << " "; 
    cout << endl; 
    return 0; 
} 
output: 
4 
0 1 2 3 

は今、それは述べてhttps://msdn.microsoft.com/en-us/library/dn956974.aspxを参照して検討しますリテラル型だけを受け入れて返す必要があります。その引数がconstexpr値であり、消費するコードがコンパイル時に戻り値を必要とする場合、たとえばconstexpr変数を初期化する場合や、非タイプのテンプレート引数を指定する場合は、コンパイル時定数が生成されます。非constexpr引数で呼び出されたとき、またはその値がコンパイル時に必要でないときは、通常の関数のように実行時に値を生成します。 (この二重の動作はconstexprのと同じ機能の非constexprのバージョンを記述する必要がなくなります。)「
は、それが有効な例を示します:機能上の

constexpr float exp(float x, int n) 
    { 
     return n == 0 ? 1 : 
     n % 2 == 0 ? exp(x * x, n/2) : 
     exp(x * x, (n - 1)/2) * x; 
    } 
関連する問題