读取没有定义的静态 const 数据成员的值:是什么支配这些规则?

Reading values of static const data members without definitions: what governs these rules?

考虑以下程序:

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 定义了 S 的静态数据成员,但是在 S::aS::b 两个整型静态数据成员中,后者指定了一个大括号- or-equal-initializer 在其 in-class 声明中,按照 [class.static.data]/4S::eS::n 不能在它们的 in-class 声明中指定大括号或等于初始化器。

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

但是,当试图从 S::nS::a 静态数据成员。

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

问题

[basic.def.odr]/4 控制变量何时为 odr-used(需要定义)。

S::eS::n 不符合例外 (4.1) 的条件,因为它们具有 non-reference 类型。它们不符合例外 (4.2) 的条件,因为它们不是 usable in constant expressions because they fail to be potentially-constant,并且它们不符合“应用 lvalue-to-rvalue 转换的 non-volatile-qualified non-class 类型”标准任何一个。它们不符合例外 (4.3) 的条件,因为它们也没有被丢弃;它们绑定到复制构造函数的引用参数。

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

S::b 属于异常 (4.2),因为它可用于常量表达式,并且会立即应用 lvalue-to-rvalue 转换(,表达式引用 S::b 会立即“转换”为 S::b 的值)。它不是 odr-used,不需要定义。

S::a 不属于异常 (4.2),因为 its initializing declaration is not reachable,这使得它在常量表达式中不可用。它是 odr-used,需要定义。