struct A {
consteval A() {};
};
constexpr bool g() {
auto a = new A;
delete a;
return true;
}
int main() {
static_assert(g());
}
https://godbolt.org/z/jsq35WxKs
GCC and MSVC reject the program, ICC and Clang accept it:
///MSVC:
<source>(6): error C7595: 'A::A': call to immediate function is not a constant expression
Compiler returned: 2
//GCC:
<source>: In function 'constexpr bool g()':
<source>:6:18: error: the value of '<anonymous>' is not usable in a constant expression
6 | auto a = new A;
| ^
<source>:6:18: note: '<anonymous>' was not declared 'constexpr'
<source>:7:12: error: type '<type error>' argument given to 'delete', expected pointer
7 | delete a;
| ^
Compiler returned: 1
Although, replacing new A
by new A()
results in GCC accepting the program as well (but not for new A{}
either).
Making at least one of the following changes results in all four compilers accepting the program:
Replace
consteval
withconstexpr
Replace
constexpr
withconsteval
Replace
auto a = new A; delete a;
with
auto alloc = std::allocator<A>{}; auto a = alloc.allocate(1); std::construct_at(a); std::destroy_at(a); alloc.deallocate(a, 1);
with
A a;
, withauto&& a = A{};
or withA{};
Only exceptions:
Clang trunk with libstdc++ seems to fail compilation with the
std::allocator
version seemingly due to an unrelated bug. With Clang 13 or libc++ it is accepted as well.In file included from <source>:1: In file included from [...]/memory:78: [...]/shared_ptr_atomic.h:459:14: error: missing 'typename' prior to dependent type name '_Atomic_count::pointer' static _Atomic_count::pointer
MSVC rejects the
std::allocator
version as long as there isconsteval
on the constructor:error C7595: 'A::A': call to immediate function is not a constant expression <source>(10): note: see reference to function template instantiation '_Ty *std::construct_at<_Ty,,void>(_Ty *const ) noexcept(false)' being compiled with [ _Ty=A ]
Replacing static_assert(g());
with g()
or removing the call completely does not seem to have any impact on these results.
Which compilers are correct and if the original is ill-formed, why is only that particular combination of qualifiers and construction method disallowed?
Motivated by the comments under this answer.
std::allocator<A>
is accepted too Demo – Adenitisconstruct
will behave like the bare new expression. – Kunnew A
tonew A()
makes GCC happy with the code. – Judaicnew A{}
. I have updated the question. – Polymericif consteval
then call theconsteval
constructor in theconstexpr
function it works, would you like to make the answer? – Stampsconsteval
function can appear anywhere (explicitly or implicitly), but independently of the context the call must by itself form a constant expression, assuming it does not appear inside anotherconsteval
function. The question here is what it means for the implicit constructor call to form a constant expression and how that interacts with thenew
-expression semantics. Coming back to this question I think the standard isn't specifying this properly, similar to how it doesn't specify the behavior ofconstexpr
variables correctly. – Polymericif consteval
puts the expression in an immediate function context, which is equivalent to just changing the function fromconstexpr
toconsteval
, which I noted in my question works fine (and this part is not surprising). What is surprising is thatnew
and the other allocation methods produce different behavior withconstexpr
. See also my comment above. – Polymeric