В С++ 17 этот код недопустим:
constexpr int foo(int i) {
return std::integral_constant<int, i>::value;
}
Это связано с тем, что даже если foo можно оценить во время компиляции, компилятору все равно необходимо создать инструкции для его выполнения во время выполнения, что делает создание экземпляра шаблона невозможным.
В C++20 у нас будет consteval функций, которые должны оцениваться во время компиляции, поэтому ограничение времени выполнения должно быть снято. Означает ли это, что этот код будет законным?
consteval int foo(int i) {
return std::integral_constant<int, i>::value;
}
cppreference имеет Спецификатор consteval объявляет функцию или шаблон функции как немедленную функцию [...] Непосредственная функция является функцией constexpr и должна удовлетворять требованиям, применимым к функциям constexpr или конструкторам constexpr, в зависимости от обстоятельств., поэтому знаки указывают на нет. Мое прочтение стандарта приводит меня к тому же выводу, но я недостаточно уверен, чтобы заявить об этом в ответе.
Этот вопрос обсуждается на мета.





Does it mean this code will be legal?
consteval int foo(int i) { return std::integral_constant<int, i>::value; }
Нет. Это все еще плохо сформировано. Хотя consteval требует, чтобы сам вызов был постоянным выражением, поэтому вы знаете, что аргумент, который производит i, должен быть постоянным выражением, foo само по себе все еще не является шаблоном. Шаблон?
Небольшое изменение в вашем примере может сделать это более очевидным:
consteval auto foo(int i) {
return std::integral_constant<int, i>();
}
Если бы это было допустимо, foo(1) и foo(2)... возвращали бы разные типы. Это совершенно другая языковая функция (параметры функции constexpr) — потому что для того, чтобы это работало, такие функции действительно должны вести себя как шаблоны.
Это может показаться немного неинтуитивным. В конце концов, если аргумент, который произвел i, был константным выражением, конечно же, i тоже можно было бы использовать как выражение? Но это все равно не так — в [expr.const] нет дополнительных исключений, разрешающих параметры для непосредственных функций. Непосредственная функция по-прежнему остается просто функцией, и ее параметры по-прежнему не являются константными выражениями — точно так же, как параметры обычной constexpr-функции не являются константными выражениями.
Конечно, с помощью int мы можем просто переписать функцию, чтобы поднять параметр функции в параметр шаблона:
template <int i>
consteval int foo() {
return std::integral_constant<int, i>::value;
}
А C++20 предоставляет нам типы классов в качестве нетиповых параметров шаблона, так что мы можем сделать это для гораздо большего числа типов, чем раньше. Но все еще существует множество типов, которые мы могли бы использовать в качестве параметра для непосредственной функции, которую мы не можем использовать в качестве параметра шаблона, поэтому это не всегда будет работать (например, std::optional или, что более интересно в C++20, std::string).
Разве мы не можем использовать помощника, такого как consteval int to_constexpr(int x) { return x; }, а затем return std::integral_constant<int, to_constexpr(i)>::value; из (не шаблонной версии) foo()? Если да, то нынешняя не-constexpr'ность параметров выглядит бесполезным ограничением.
@ Руслан Нет. Потому что to_constexpr(x) не является постоянным выражением, которое разрешено, поскольку оно находится внутри определения функции consteval.
Нет.
Какие бы изменения ни повлекла за собой статья, а это немного в этот момент, они не могут изменить того факта, что определение функции, не являющееся шаблоном, печатается только один раз. Более того, если предложенный вами код будет законным, мы, вероятно, могли бы найти способ объявить переменную типа std::integral_constant<int, i>, что кажется очень запретительным с точки зрения ODR.
В документе также указывается, что параметры не предназначены для обработки в качестве основных константных выражений в одном из его примеров;
consteval int sqrsqr(int n) {
return sqr(sqr(n)); // Not a constant-expression at this point,
} // but that's okay.
Короче говоря, параметры функции никогда не будут постоянными выражениями из-за возможного несоответствия типизации.
Обратите внимание, что он позволяет передавать аргумент функции consteval другой функции consteval, даже если аргумент технически не является постоянным выражением.
@NicolBolas Это разрешено на стороне немедленных функций, а не на стороне аргументов. Как вы сказали: это не постоянные выражения, и параметры шаблона требуют их по уважительной причине.
Казалось бы, это будет недопустимо в C++20. Хорошее объяснение того, почему это было бы проблематично поддерживать, уже было дано в ответах @Barry и @Columbo (на самом деле это не работает с системой типов). Я просто добавлю то, что я считаю соответствующими цитатами из стандарта, которые на самом деле делают это незаконным.
На основе [temp.arg.nontype]/2
A template-argument for a non-type template-parameter shall be a converted constant expression […]
Преобразованное константное выражение — это константное выражение, которое неявно преобразуется в определенный тип [expr.const]/7 (здесь — тип параметра шаблона). Таким образом, ваш вопрос сводится к вопросу о том, является ли переменная внутри функции consteval постоянным выражением. На основе [expr.const]/8
A constant expression is either a glvalue core constant expression that refers to an entity that is a permitted result of a constant expression (as defined below), or a prvalue core constant expression whose value satisfies the following constraints: […]
Выражение i — это glvalue id-выражение, которое является основным константным выражением (поскольку его вычисление не делает ничего из перечисленного в [expr.const]/4). Однако объект, на который ссылается это основное постоянное выражение, не является разрешенным результатом постоянного выражения [expr.const]/8:
An entity is a permitted result of a constant expression if it is an object with static storage duration that either is not a temporary object or is a temporary object whose value satisfies the above constraints, or if it is a non-immediate function.
Рассматриваемый объект не имеет статической длительности хранения и не является временным объектом…
АФАИК, нет.
i(пока) не считается основным постоянным выражением в тот самый момент.