读取没有定义的静态常量数据成员的值:这些规则受什么约束?

发布于 2025-01-13 05:59:38 字数 2054 浏览 0 评论 0原文

考虑以下程序:

struct Empty    {          };
struct NonEmpty { int x{}; };

struct S {
    static const Empty     e;   // declaration
    static const NonEmpty  n;   // declaration
    static const int       a;   // declaration
    static const int       b{}; // declaration and initializer
};

Empty    ge;  // declaration & definition
NonEmpty gn;  // declaration & definition
int      ga;  // declaration & definition

int main() {
    auto le1 = S::e; // OK
    auto ln1 = S::n; // #1 error: undefined reference
    auto la1 = S::a; // #2 error: undefined reference
    auto lb1 = S::b; // OK

    auto le2 = ::ge;  // OK
    auto ln2 = ::gn;  // OK
    auto la2 = ::ga;  // OK
}

没有定义 S 的静态数据成员,而是定义了两个整型静态数据成员 S::aS:: b 后者在其类内声明中指定了一个大括号或等于初始化器,根据 [class.static.data]/4S::eS::n 不得在其类内声明中指定括号或等于初始化器:s。

S 的所有这些静态数据成员都像它们的全局命名空间范围对应变量一样具有 gegnga ,静态存储时间。根据 [basic.start.static]/2 因此,作为静态初始化的一部分,它们都经历零初始化。

然而,当尝试复制初始化局部变量时,上面的程序在 #1#2 被 GCC 和 Clang(各种语言版本、各种编译器版本)拒绝来自 S::nS::a 静态数据成员。

我假设编译器是正确的,但我找不到支持这种拒绝的规范部分,或者更确切地说,为什么程序接受空类静态数据成员 S::e 的情况。 S::b 的情况被接受“有道理”,但我再次无法规范地解决这个问题(odr-use,conv.lval, basic.life,...?)。

问题

  • 什么规范文本解释了为什么上面的程序拒绝读取 S::eS::a 的值,因为格式不正确,而它接受读取 S::e 的值code>S::e (空)和 S::b (已初始化)?比如说,在 N4868 中。

Consider the following program:

struct Empty    {          };
struct NonEmpty { int x{}; };

struct S {
    static const Empty     e;   // declaration
    static const NonEmpty  n;   // declaration
    static const int       a;   // declaration
    static const int       b{}; // declaration and initializer
};

Empty    ge;  // declaration & definition
NonEmpty gn;  // declaration & definition
int      ga;  // declaration & definition

int main() {
    auto le1 = S::e; // OK
    auto ln1 = S::n; // #1 error: undefined reference
    auto la1 = S::a; // #2 error: undefined reference
    auto lb1 = S::b; // OK

    auto le2 = ::ge;  // OK
    auto ln2 = ::gn;  // OK
    auto la2 = ::ga;  // OK
}

None of the static data members of S are defined, but of the two integral type static data members S::a and S::b the latter specifies a brace-or-equal-initializer at its in-class declaration, as it may, as per [class.static.data]/4. S::e and S::n may not specify brace-or-equal-initializer:s at their in-class declaration.

All these static data members of S have, like their global namespace-scope counterpart variables ge, gn and ga, static storage duration. As per [basic.start.static]/2 they all thus undergo zero initialization as part of static initialization.

The program above is, however, rejected by GCC and Clang (various language versions, various compiler versions) at #1 and #2, when trying to copy-initialize local variables from the S::n and S::a static data members.

I'm assuming the compilers are correct, but I cannot find the normative section which supports this rejection, or maybe rather, why the program accept the case with the empty class static data member S::e. That the case of S::b is accepted "makes sense", but again I can't sort this out normatively (odr-use, conv.lval, basic.life, ... ?).

Question

  • What normative text explains why the program above rejects reading the value of S::e and S::a as ill-formed, whilst it accepts read the values of S::e (empty) and S::b (initialized)? Say, in N4868.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

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

发布评论

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

评论(1

巾帼英雄 2025-01-20 05:59:39

[basic.def.odr]/4 管辖当变量被 odr 使用时(需要定义)。

S::eS::n 不符合例外 (4.1) 的条件,因为它们具有非引用类型。它们不符合例外 (4.2) 的条件,因为它们不能 在常量中使用表达式 因为它们不能是 潜在恒定,并且它们也不符合“应用左值到右值转换的非易失性限定非类类型”标准。它们不符合例外 (4.3) 的条件,因为它们也没有被丢弃;它们绑定到复制构造函数的引用参数。

这意味着 S::eS::n 是 odr 使用的。您仅获得了 S::n 的诊断结果,而不是 S::e 的诊断结果。这并不意味着您对 S::e 的使用是可以接受的。诊断 ODR 违规行为不需要实施。您使用的编译器可能省略了对 Empty 的(简单的)复制构造函数的调用,并生成了链接器无法知道 S::e 的目标文件> 应该已经被定义了。

S::b 属于异常 (4.2),因为它可在常量表达式中使用,并且立即应用左值到右值的转换(ie,该表达式引用S::b 立即“转换”为 S::b 的值)。它不被 odr 使用,并且不需要定义。

S::a 不属于异常 (4.2),因为 其初始化声明不可访问,这使得它无法在常量表达式中使用。它是 odr 使用的,需要一个定义。

[basic.def.odr]/4 governs when a variable is odr-used (requiring a definition).

S::e and S::n are not eligible for exception (4.1) because they have non-reference type. They are not eligible for exception (4.2) because they are not usable in constant expressions because they fail to be potentially-constant, and they do not meet the "non-volatile-qualified non-class type to which the lvalue-to-rvalue conversion is applied" criterion either. They are not eligible for exception (4.3) because they are not discarded either; they are bound to the reference parameters of the copy constructors.

This implies that S::e and S::n are odr-used. You only got a diagnostic for S::n, not S::e. This does not imply that your use of S::e was acceptable. The implementation is not required to diagnose a violation of the ODR. The compilers you have used have probably elided the call to the (trivial) copy constructor of Empty, and generated object files where the linker has no way to know that S::e was supposed to have been defined.

S::b falls under exception (4.2) because it is usable in constant expressions, and the lvalue-to-rvalue conversion is immediately applied (i.e., the expression referring to S::b is "converted" into the value of S::b immediately). It is not odr-used, and does not need a definition.

S::a does not fall under exception (4.2) because its initializing declaration is not reachable, which has made it not usable in constant expressions. It is odr-used, and needs a definition.

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