在成员初始化中对自我分配的持续评估
在以下程序中,constexpr
function foo()
用字段 x = 1 ,使对象成为a
的对象然后使用std :: struct_at
和默认初始化x = x
,然后打印了常数评估值:
#include <memory>
#include <iostream>
struct A {
int x = x;
};
constexpr int foo() {
A a{1};
std::construct_at<A>(&a);
return a.x;
}
constexpr int v = foo();
int main() {
std::cout << v;
}
GCC打印1 在这里。 Clang和MSVC打印
0
。并且只有叮当声发出警告:字段'x'使用
时不专门化。演示: https://gcc.godbolt.org/z/wtsxxxdrj8e
在程序中?如果是,为什么没有编译器在持续评估期间检测到它?如果没有,哪个编译器是对的?
In the following program, constexpr
function foo()
makes an object of A
with the field x=1
, then constructs another object on top of it using std::construct_at
and default initialization x=x
, then the constant evaluated value is printed:
#include <memory>
#include <iostream>
struct A {
int x = x;
};
constexpr int foo() {
A a{1};
std::construct_at<A>(&a);
return a.x;
}
constexpr int v = foo();
int main() {
std::cout << v;
}
GCC prints 1
here. Both Clang and MSVC print 0
. And only Clang issues a warning: field 'x' is uninitialized when used
. Demo: https://gcc.godbolt.org/z/WTsxdrj8e
Is there an undefined behavior in the program? If yes, why does no compiler detect it during constant evaluation? If no, which compiler is right?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
C ++ 20 [basic.life] /1.5指出,当对象的寿命(在这种情况下,对象
a
)在该标准尚不清楚何时确切地将内存视为“重复使用”(因此,旧
a
的寿命结束),但[Into.Object]/1指出我认为,默认会员initializer
= x
的评估是在新的a
对象的“构建”期间发生的事情,这意味着此处是要点,新的a
对象已经存在(但其寿命尚未开始),并且旧对象的寿命已经结束。这意味着新的a
的初始化读取其x
成员的值,其寿命尚未开始,因为其初始化尚未完成,这违反了[basic.life]/ 7.1,将是UB。在C ++ 20中,
foo
的定义违反了[dcl.constexpr]/6:这意味着不需要编译器为您的程序发布诊断。
在C ++ 23中,该规则将被废除(请参见 p2448 ),以便您可以争论编译器必须发行A诊断如果他们声称C ++ 23合规性。但是,没有任何编译器能够以恒定表达方式诊断各种核心语言UB(例如,似乎特别困难的诊断的东西是未序列的写作或涉及同一标量对象的未续写的读写),所以不要保持您的呼吸要固定。
C++20 [basic.life]/1.5 states that the lifetime of an object (in this case, the object
a
) ends whenThe standard isn't totally clear about when exactly the memory is considered "reused" (and thus, the old
A
's lifetime ends) but [intro.object]/1 states thatIn my opinion, the evaluation of the default member initializer
= x
is something that happens during the "period of construction" of the newA
object, and that means that at that point, the newA
object has already come into existence (but its lifetime has not yet begun), and the old object's lifetime has already ended. That means the initialization of the newA
reads the value of itsx
member, whose lifetime has not begun because its initialization is not complete, which violates [basic.life]/7.1 and would be UB.In C++20, the definition of
foo
violates [dcl.constexpr]/6:This means compilers are not required to issue a diagnostic for your program.
In C++23, this rule will be abolished (see P2448) so you can argue that compilers must issue a diagnostic if they claim C++23 compliance. However, no compiler has ever been able to diagnose all kinds of core language UB in constant expressions (for example, something that seems particularly difficult to diagnose is unsequenced writes or an unsequenced read and write involving the same scalar object) so don't hold your breath for it to be fixed.