Означает ли это, что этот код будет законным?
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
).