Since C++20, std::vector
can be used in constant expressions. And as far as I known, current C++ permits dynamically allocate memory under the condition that any such allocation is deallocated by the time the constant expression is "over".
However, I encounter that in case of immediate function the rules might be different. Please consider the example:
consteval auto getVec() {
return std::vector<int>(9);
}
static_assert( getVec().size() == 9 );
Here immediate consteval
function getVec
returns not-empty std::vector
, size of which is verified in a constant expression.
I expected that this code would compile, because all deallocations must be done automatically, and indeed it is accepted in Clang with libc++
.
But MSVC complains:
error C7595: 'getVec': call to immediate function is not a constant expression
note: (sub-)object points to memory which was heap allocated during constant evaluation
fatal error C1903: unable to recover from previous error(s); stopping compilation
and GCC behaves similarly:
error: 'getVec()()' is not a constant expression because it refers to a result of 'operator new'
Online demo: https://gcc.godbolt.org/z/d736qr3hh
Which compiler is correct here?
This is fixed with P2564R3 being accepted as a defect report for C++20. It allows getVec()
to be a subexpression of a larger constant expression without being a constant expression by itself.
LLVM 17 and the upcomming GCC 14 implement it. For MSVC there is a feature request.
The original reason for the problem was this:
A potentially-evaluated id-expression that denotes an immediate function ([dcl.constexpr]) shall appear only
- as a subexpression of an immediate invocation, or
- in an immediate function context ([expr.const])
An expression or conversion is in an immediate function context if it is potentially evaluated and its innermost non-block scope is a function parameter scope of an immediate function. An expression or conversion is an immediate invocation if it is 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.
static_assert( getVec().size() == 9 );
is not in an immediate function context. Therefore, the explicit invocation getVec()
is an immediate invocation, so it needs to produce a constant expression. getVec()
by itself is not a constant expression since it doesn't deallocate the memory allocated by new.
For comparison, the following do compile https://godbolt.org/z/9KqhY7oP8
consteval auto getVec() {
return std::vector<int>(9);
}
consteval auto getVecSize() {
return getVec().size();
}
static_assert(getVecSize() == 9);
static_assert([]() consteval { return getVec().size() == 9; }());
consteval void InAnImmediateFunctionContext() {
static_assert(getVec().size() == 9);
}