【发布时间】:2022-12-21 02:38:24
【问题描述】:
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 和 MSVC 拒绝该程序,ICC 和 Clang 接受它:
///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
虽然,将 new A 替换为 new A() 会导致 GCC 也接受该程序(但也不接受 new A{})。
至少进行以下更改之一会导致所有四个编译器都接受该程序:
-
将
consteval替换为constexpr -
将
constexpr替换为consteval -
代替
auto a = new A; delete a;和
auto alloc = std::allocator<A>{}; auto a = alloc.allocate(1); std::construct_at(a); std::destroy_at(a); alloc.deallocate(a, 1);与
A a;、auto&& a = A{};或A{};只有例外:
-
带有 libstdc++ 的 Clang 主干似乎由于不相关的错误而无法使用
std::allocator版本进行编译。对于 Clang 13 或 libc++,它也被接受。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 -
只要构造函数上有
consteval,MSVC 就会拒绝std::allocator版本: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 ]
将
static_assert(g());替换为g()或完全删除调用似乎对这些结果没有任何影响。
哪些编译器是正确的,如果原始编译器格式错误,为什么只不允许限定符和构造方法的特定组合?
受到 this answer 下的 cmets 的激励。
-
【问题讨论】:
-
分配器版本实际上并不初始化对象。我估计调用
construct将表现得像裸露的 new 表达式。 -
有趣的是,将
new A更改为new A()让 GCC 对代码感到满意。 -
@SolomonUcko 这个 bug 似乎在几个版本前就已经修复了:godbolt.org/z/qcxhvefxv
-
我不阅读标准论文来验证我的想法。但据我了解:consteval 必须仅在编译时上下文中使用。由于 constexpr 可以在编译时和运行时使用,它会拒绝 consteval 表达式。有趣的是,我像这样更改了 g 函数: constexpr bool g() { if constexpr( std::is_constant_evaluated() ) { auto a = new A;删除一个; } 返回真;但代码在 MSVC 17.3.5(C++ 最新版本)下仍然被拒绝。
-
@TeaAgeSolutions 不,对
consteval函数的函数调用可以出现在任何地方(显式或隐式),但与上下文无关,调用本身必须形成常量表达式,假设它没有出现在另一个consteval函数中。这里的问题是隐式构造函数调用形成常量表达式意味着什么,以及它如何与new-表达式语义交互。回到这个问题,我认为标准没有正确指定这一点,类似于它没有正确指定constexpr变量的行为。
标签: c++ language-lawyer c++20 constexpr consteval