The following program
template<class T>
consteval auto foo(const T&) {
return 0;
}
template<class T>
consteval auto bar(const T& t) {
auto n = foo(t);
return n;
}
int main() {
static_assert(foo("abc") == 0);
static_assert(bar("abc") == 0);
}
is built fine in GCC, but Clang rejects it with the messages:
error: call to consteval function 'foo<char[4]>' is not a constant expression
note: in instantiation of function template specialization 'bar<char[4]>' requested here
static_assert(bar("abc") == 0);
note: function parameter 't' with unknown value cannot be used in a constant expression
auto n = foo(t);
Demo: https://gcc.godbolt.org/z/M6GPnYdqb
Is it some bug in Clang?
On line 9 it calls the nested function within the func () function and then the nested function is executed. So in this manner, the nested function is called every time we call the func () function automatically because it is called inside the func () function.
The consteval specifier declares a function or function template to be an immediate function, that is, every potentially evaluated call (i.e. call out of an unevaluated context) to the function must (directly or indirectly) produce a compile time constant expression .
So in this manner, the nested function is called every time we call the func () function automatically because it is called inside the func () function. Python Closures or you can say nested function objects can be used to protect or filter some functionalities inside that function.
Nesting level limits A formula can contain up to seven levels of nested functions. When one function (we'll call this Function B) is used as an argument in another function (we'll call this Function A), Function B acts as a second-level function.
This is a clang bug. gcc and msvc are correct to accept it.
There are two relevant rules in question:
All immediate invocations must be constant expressions. This comes from [expr.const]/13:
An expression or conversion is in an immediate function context if it is potentially evaluated and either:
- its innermost enclosing non-block scope is a function parameter scope of an immediate function, or
- its enclosing statement is enclosed ([stmt.pre]) by the compound-statement of a consteval if statement ([stmt.if]).
An expression or conversion is an immediate invocation if it is a potentially-evaluated explicit or implicit invocation of an immediate function and is not in an immediate function context. An immediate invocation shall be a constant expression.
And touching an unknown reference is not allowed in constant expressions (this is [expr.const]/5.13):
An expression E is a core constant expression unless the evaluation of E, following the rules of the abstract machine ([intro.execution]), would evaluate one of the following: [...]
- an id-expression that refers to a variable or data member of reference type unless the reference has a preceding initialization and either
- it is usable in constant expressions or
- its lifetime began within the evaluation of E;
For more on this latter rule, see my post on the the constexpr array size problem and my proposal to resolve this (hopefully for C++23).
Okay, back to the problem. foo
is obviously fine, it doesn't do anything.
In bar
, we call foo(t)
. This is not a constant expression (because t
is an unknown reference), but we are in an immediate function context (because bar
is consteval
), so it doesn't matter that foo(t)
is not a constant expression. All that matters is that bar("abc")
is a constant expression (since that is an immediate invocation), and there's no rule we're violating there. It is pretty subtle, but the reference t
here does have its lifetime begin within the evaluation of E
-- since E
here is the call bar("abc")
, not the call foo(t)
.
If you mark bar
constexpr
instead of consteval
, then the foo(t)
call inside of it becomes an immediate invocation, and now the fact that it is not a constant expression is relevant. All three compilers correctly reject in this case.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With