在成员初始化中对自我分配的持续评估

发布于 2025-01-23 14:32:11 字数 745 浏览 0 评论 0原文

在以下程序中,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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

鹿童谣 2025-01-30 14:32:11

C ++ 20 [basic.life] /1.5指出,当对象的寿命(在这种情况下,对象a)在

对象占据的存储已释放,或者是由未嵌套在o(6.7.2)的对象重复使用的。

该标准尚不清楚何时确切地将内存视为“重复使用”(因此,旧a的寿命结束),但[Into.Object]/1指出

...一个物体在其施工期(11.10.5),整个生命周期(6.7.3)及其破坏时期(11.10.5)占据了一个存储区域(11.7.3)。

我认为,默认会员initializer = x的评估是在新的a对象的“构建”期间发生的事情,这意味着此处是要点,新的a对象已经存在(但其寿命尚未开始),并且旧对象的寿命已经结束。这意味着新的a的初始化读取其x成员的值,其寿命尚未开始,因为其初始化尚未完成,这违反了[basic.life]/ 7.1,将是UB。

在C ++ 20中,foo的定义违反了[dcl.constexpr]/6:

既不默认或模板的constexPR函数,也不需要诊断,如果无法评估在评估任何有效的恒定恒定评估表达式的同时,对函数的调用进行评估。 /p>

这意味着不需要编译器为您的程序发布诊断。

在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 when

the storage which the object occupies is released, or is reused by an object that is not nested within o (6.7.2).

The 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 that

... An object occupies a region of storage in its period of construction (11.10.5), throughout its lifetime (6.7.3), and in its period of destruction (11.10.5).

In my opinion, the evaluation of the default member initializer = x is something that happens during the "period of construction" of the new A object, and that means that at that point, the new A 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 new A reads the value of its x 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:

A constexpr function that is neither defaulted nor a template is ill-formed, no diagnostic required, if it is not possible for an evaluation of an invocation of the function to be performed while evaluating any valid manifestly constant-evaluated expression.

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文