Шаблоны, массивы и постоянные выражения

Рассмотрим код ниже:

template<char>
struct S { }; 

template<int N>
constexpr auto f(const char (&ref) [N]) {
    return S<ref[0]>{};
}

int main() {
    constexpr auto v = f("foo");
    (void)v;
}

Он не компилируется, поскольку ref[0] не является постоянным выражением.

В любом случае, приведенный ниже код компилируется нормально:

template<int N>
constexpr auto f(const char (&ref) [N]) {
    return ref[0];
}

int main() {
    constexpr auto v = f("foo");
    (void)v;
}

Должны ли они оба компилироваться или не делать этого по более или менее одной и той же причине?

Из [expr.const] мы получаем следующее:

Условное выражение e является основным константным выражением, если только оценка e [...] не оценила бы одно из следующих выражений:
[...]
- id- выражение, которое относится к переменной или члену данных ссылочного типа, если только ссылка не имеет предшествующей инициализации и либо
- она ​​инициализируется константным выражением, либо
- ее время жизни началось в пределах оценки e;

В любом случае, в этом случае он инициализируется постоянным выражением и время жизни такое же, как у e, поэтому правило не применяется.

Что не так в моих рассуждениях?

В качестве побочного вопроса я бы спросил, можно ли использовать такой массив или его часть в качестве аргумента шаблона.


person skypjack    schedule 17.10.2016    source источник
comment
Обратите внимание, что функция constexpr все еще может быть вызвана во время выполнения, а S<ref[0]> не может быть создан во время выполнения.   -  person kennytm    schedule 17.10.2016
comment
@kennytm Обратите внимание, что я называю это constexpr auto v = f("foo");.   -  person skypjack    schedule 17.10.2016
comment
Неважно, кто-то другой мог назвать это как char a[10]; randomize(a); f(a).   -  person kennytm    schedule 17.10.2016
comment
@kennytm Я понимаю вашу точку зрения. Нет возможности использовать его как параметр шаблона?   -  person skypjack    schedule 17.10.2016
comment
Я не думаю, что это возможно в общем случае, но если вы используете g ++, вы можете использовать его расширение в строке буквальный   -  person W.F.    schedule 17.10.2016


Ответы (2)


Этот:

template<int N>
constexpr auto f(const char (&ref) [N]) {
    return S<ref[0]>{};
}

неправильно сформирован, потому что согласно [temp.arg.nontype]:

аргумент-шаблон для не-типа параметр-шаблона должен быть преобразованным постоянным выражением (5.20) типа параметра-шаблона .

и из [expr.const]:

условное-выражение e является основным постоянным выражением, если только оценка e, следуя правилам абстрактной машины (1.9), не оценила бы одно из следующих выражений: [...]
(2.7) - преобразование lvalue-to-rvalue (4.1), если оно не применяется к
(2.7.1) - энергонезависимое glvalue целочисленного или перечисляемого типа, которое относится к полному энергонезависимому константному объекту с предшествующей инициализации, инициализируется константным выражением, или
(2.7.2) - энергонезависимое значение glvalue, которое относится к подобъекту строкового литерала (2.13.5), или
(2.7.3) - a энергонезависимое значение glvalue, которое относится к энергонезависимому объекту, определенному с помощью constexpr, или которое относится к неизменяемому подобъекту такого объекта, или
(2.7.4) - энергонезависимое значение glvalue литерального типа это относится к энергонезависимому объекту, время жизни которого началось в пределах вычисления e;

ref[0] требует преобразования lvalue-to-rvalue, и ни один из этих подпунктов не применим. Обратите внимание, что ref не является строковым литералом, поэтому 2.7.2 не применяется и не определяется с помощью constexpr, потому что это аргумент функции, а у нас нет такой возможности.

Нам в основном нужна возможность передавать строковые литералы как литералы, чего пока не существует.


Другой пример:

template<int N>
constexpr auto f(const char (&ref) [N]) {
    return ref[0];
}

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

person Barry    schedule 17.10.2016

Первый пример не должен компилироваться, потому что у вас не может быть функций constexpr только для времени компиляции (или перегрузки во время компиляции, например D's _ 1_).

Следуя этому рассуждению, если вы вызвали f первого примера во время выполнения, каков был бы его тип возврата?

Что касается побочного вопроса: Boost Hana, несмотря на поддержку только новейшего стандарта, использует только строковые литералы для материала времени выполнения, поэтому это может быть невозможно.

person krzaq    schedule 17.10.2016