web-dev-qa-db-ja.com

なぜこれが定数式ではないのですか?

この些細な例では、_test2_が成功したとしても_test1_はコンパイルに失敗し、なぜそうなのかわかりません。 _arr[i]_がconstexprとマークされた関数からの戻り値に適している場合、それを非型テンプレート引数として使用できないのはなぜですか?

_template<char c>
struct t
{ 
    static const char value = c;
};

template <unsigned N>
constexpr char test1(const char (&arr)[N], unsigned i)
{
    return arr[i];
}

template <unsigned N>
constexpr char test2(const char (&arr)[N], unsigned i)
{
    return t<arr[i]>::value;
}

int main()
{
   char a = test1("Test", 0); //Compiles OK
   char b = test2("Test", 0); //error: non-type template argument 
                              //is not a constant expression
}
_

編集:これは違いはありません:

_template<char c>
struct t
{ 
    static const char value = c;
};

template <unsigned N>
constexpr char test1(const char (&arr)[N])
{
    return arr[0];
}

template <unsigned N>
constexpr char test2(const char (&arr)[N])
{
    return t<arr[0]>::value;
}

int main()
{
   char a = test1("Test"); //Compiles OK
   char b = test2("Test"); //error: non-type template argument 
                           //is not a constant expression
}
_
20
Chris_F

簡単な答え:_C++11/14_にはconstexpr関数パラメーターはありません。

より長い答え:test1()では、iがコンパイル時定数でない場合、関数は実行時にまだ使用可能です。しかし、test2()では、iがコンパイル時定数であるかどうかをコンパイラーに認識できませんが、関数をコンパイルするために必要です。

例えば。 _test1_の次のコードはコンパイルされます

_int i = 0;    
char a = test1("Test", i); // OK, runtime invocation of test1()

constexpr int i = 0;
constexpr char a = test1("Test", i); // also OK, compile time invocation of test1()
_

あなたのtest2()を単純に

_constexpr char test3(unsigned i)
{
    return t<i>::value;
}
_

test3(0)の内部では、i無条件コンパイル時の式であることを証明できないため、これはtest3()に対してコンパイルされません。それを表現するには、constexpr関数パラメーターが必要です。

標準からの引用

5.19定数式[expr.const]

2条件式eは、抽象マシン(1.9)の規則に従ってeの評価が以下の式のいずれかを評価しない限り、コア定数式です。

—参照に先行する初期化と次のいずれかがない限り、参照型の変数またはデータメンバーを参照するid-expression
—定数式または

— eの評価内で有効期間が開始されたオブジェクトの非静的データメンバーです。

このセクションには、質問に対応する次のコード例があります。

_constexpr int f1(int k) {
    constexpr int x = k; // error: x is not initialized by a
                         // constant expression because lifetime of k
                         // began outside the initializer of x
    return x;
}
_

上記の例のxは定数式ではないため、_f1_内でxまたはkのいずれかを使用してテンプレートをインスタンス化できないことを意味します。

22
TemplateRex

ここでconstexprが何をするかについての誤解があります。関数はコンパイル時に適切な引数に対して評価可能でなければならないことを示しますが、一般的なケースではまだコンパイルするための要件をnot削除します。

最初のバージョンを見てみましょう:

template <unsigned N>
constexpr char test1(const char (&arr)[N], unsigned i) {
    return arr[i];
}

さて、これは明らかにコンパイル時の評価です:

enum { CompileTimeConstant = test1("Test", 0) };

あなたの例may beですが、オプティマイザー/ QoIの問題です:

char MayBeCompileTimeConstant = test1("Test", 0);

この例は明らかにそうではありませんが、まだ評価可能である必要があります

char arr[10];
int i;
std::cin >> i;
std::cin >> arr;
char b = test1(arr, i);
std::cout << "'" << arr << "'[" << i << "] = " << b << '\n';

test2は、最後のケースではおそらくコンパイルできません。まったくコンパイルできません。 (コードがgoodであることを示唆していないことに注意してください)。

7
Useless

ここでの問題は、arr[i]は添字演算子を呼び起こしますoperator[]この演算子は定数式を返しません。

それは実際にconstexprの問題ではなく、テンプレート引数の推論の問題です。非型テンプレート引数は、添字演算子の戻り引数ではない定数式でなければなりません。

したがって、コンパイラはarr[i]は定数式ではありません。

2
101010

なぜならarr[i]はコンパイル時の定数式ではありません。実行時に異なる場合があります。

1
user1742529