Почему это не постоянное выражение?

В этом тривиальном примере 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
}

Ответ 1

Короткий ответ: в 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), потому что внутри test3() невозможно доказать, что i является выражением безусловного компиляции. Вам нужны параметры функции constexpr, чтобы выразить это.

Цитата из стандартного

5.19 Константные выражения [expr.const]

2 Условное выражение e является выражением постоянной константы, оценка e, следуя правилам абстрактной машины (1.9), будет оценивать одно из следующих выражений:

- id-выражение, которое ссылается на переменную или элемент данных ссылочный тип, если ссылка не имеет предшествующей инициализации и либо
 - он инициализируется постоянным выражением или

- это нестатический член данных объекта, время жизни которого началось с оценки 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 в приведенном выше примере не является константным выражением, это означает, что вы не можете создавать шаблоны с помощью x или k внутри f1.

Ответ 2

Там заблуждение о том, что constexpr здесь. Это указывает на то, что функция должна быть оценена во время компиляции для подходящих аргументов, но она не устраняет необходимость компиляции в общем случае.

Возьмем первую версию:

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

Теперь это явно оценка времени компиляции:

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

ваш пример может быть, но это проблема оптимизатора /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 невозможно компилировать для последнего случая, он вообще не может скомпилироваться. (Обратите внимание, что я не предлагаю, чтобы код был хорошим).

Ответ 3

Проблема здесь в том, что вызов arr[i] вызывает индексный оператор operator[]. Этот оператор не возвращает константное выражение.

Это не проблема constexpr на самом деле, это проблема вывода аргумента шаблона. Аргумент типа Non type должен быть константным выражением, которое не является аргументом return оператора индекса.

Поэтому компилятор справедливо жалуется, что arr[i] не является постоянным выражением.

Ответ 4

Потому что arr[i] не является постоянным выражением времени компиляции. Это может быть разным во время выполнения.