2017-08-01 11 views
17

は、次のコードを考えてみましょう:定数式に式を表示できないのはなぜですか?

template<int value> 
constexpr int foo = value; 

template<typename... Ts> 
constexpr int sum(Ts... args) { 
    return foo<(args + ...)>; 
} 

int main() { 
    static_assert(sum(10, 1) == 11); 
} 

打ち鳴らす4.0.1は私に次のエラーを与える:

main.cpp:6:17: error: non-type template argument is not a constant expression 
    return foo<(args + ...)>; 
       ^~~~ 

これは私を驚かせました。すべての引数はコンパイル時に知られていますが、sumconstexprとマークされているため、コンパイル時にfold式を評価できない理由はありません。

当然のことながら、これはまた、同じエラーメッセージで失敗します。

constexpr int result = (args + ...); // in sum 

[expr.prim.fold]、それは非常に短いですし、唯一許さ構文について説明し、非常に有用ではありません。

clangの新しいバージョンを試しても、gccと同じ結果が得られます。

実際に許可されているかどうか

+0

倍の表現はここでは煙草です。 – bolov

+3

@bolov赤ちゃんを意味しますか? – Oktalist

+0

@Oktalistはい。ありがとう、ちょうど私が探していたイディオムです。私はちょうど次善のものに行きました。 – bolov

答えて

12

定数式にfold式を含めることができます。関数呼び出しがそれ自体が定数式の一部でない限り、ではなく、では、関数パラメータの値を使用することができます。一例として:

constexpr int foo(int x) { 
    // bar<x>(); // ill-formed 
    return x; // ok 
} 
constexpr int y = foo(42); 

定数式で初期化する必要がy変数。 foo(42)を呼び出しても、その値を返すためにパラメータxに対してlvalue-to-rvalue変換を実行しても、そのパラメータは内に作成され、その値は静的に既知であるため、しかしx自体はfooの定数式ではありません。それにもかかわらず、発生する文脈において定数式ではない式は、より大きな定数式の一部であり得る。

非型テンプレートパラメータの引数は、それ自身の定数式でなければなりませんが、xはそうではありません。コメントアウトされた行は不正な形式です。

同様に、のパラメータに対してlvalue-to-rvalue変換を実行するため、(args + ...)は定数式でないため(テンプレート引数として使用できません)。しかし、関数sumが定数式の引数で呼び出された場合は、(args + ...)が含まれていても、関数呼び出し全体が定数式になります。

5

この質問の読者の中には、OP:sの例をコンパイルして期待どおりに実行するための方法を知りたいと思っている人がいますので、この補遺をBrian:s excellent accepted answerに含めています。

ブライアンは説明したよう、可変引数関数パラメータの値は、sum内の定数式ではありません(ただし、パラメータがfooの範囲を「エスケープ」しないようfooは限り定数式ではないために発生しません。定数式foo(42)内に作成されているため)。OPにこの知識を適用する

:S例えば、代わりのsumconstexpr即時範囲を脱出するとき、我々は、可変長非なるように可変引数関数のパラメータを移行することができるconstexprとして処理されない可変引数関数のパラメータを使用して-typeテンプレートパラメータ。

template<auto value> 
constexpr auto foo = value; 

template<auto... args> 
constexpr auto sum() { 
    return foo<(args + ...)>; 
} 

int main() { 
    static_assert(sum<10, 1, 3>() == 14); 
} 
3

問題は...とは関係ありません。

template<class T0, class T1> 
constexpr int sum(T0 t0, T1 t1) { 
    return foo<(t0+t1)>; 
} 

これも同じように失敗します。

本質的には、constexpr関数を非constexpr引数で呼び出し可能にする必要があるという問題があります。

constexprが意味することはよくある誤解です。「常にconstexpr」という意味ではありません。

ここで何がうまくいかないのかという複雑な標準句がありますが、本質的にはconstexpr関数内では、関数の引数そのものはconstexprとはみなされません。関数の結果は、入力が0であればになりますが、関数内では、引数がconstexprでなくても有効でなければなりません。

これを回避するには、整数を解析するリテラル""_kを定義し、integral_constantを生成します。整数定数の+constexprされた変数に依存しないため

static_assert(sum(10_k, 1_k) == 11); 

は、コンパイルして実行します。あるいは、値を非型テンプレート・パラメーターとして使用することもできます。 static_assert(合計< 10,1>()== 11);

関連する問題