Does it mean this code will be legal?
consteval int foo(int i) {
return std::integral_constant<int, i>::value;
}
No. This is still ill-formed. While consteval
requires the call itself to be a constant expression, so you know that the argument that produces i
must be a constant expression, foo
itself is still not a template. Template?
A slight variation in your example might make this more obvious:
consteval auto foo(int i) {
return std::integral_constant<int, i>();
}
Were this to be valid, foo(1)
and foo(2)
would... return different types. This is an entirely different language feature (constexpr function parameters) - because in order for this to work, such functions would really need to behave like templates.
It may seem a little unintuitive. After all, if the argument that produced i
was a constant expression, surely i
should be usable as one as well? But it's still not - there are no additional exceptions in [expr.const] that permit parameters for immediate functions. An immediate function is still just a function, and its parameters still aren't constant expressions -- in the same way that a normal constexpr
function's parameters aren't constant expressions.
Of course with int
, we can just rewrite the function to lift the function parameter into a template parameter:
template <int i>
consteval int foo() {
return std::integral_constant<int, i>::value;
}
And C++20 gives us class types as non-type template parameters, so we can actually do this for many more types than we could before. But there are still plenty of types that we could use as a parameter to an immediate function that we cannot use as a template parameter - so this won't always work (e.g. std::optional
or, more excitingly in C++20, std::string
).
i
is (yet) not considered a core constant expression at that very moment. – Marjoram