读取没有定义的静态 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::a
和 S::b
两个整型静态数据成员中,后者指定了一个大括号- or-equal-initializer 在其 in-class 声明中,按照 [class.static.data]/4。 S::e
和 S::n
不能在它们的 in-class 声明中指定大括号或等于初始化器。
S
的所有这些静态数据成员,就像它们的全局命名空间作用域对应变量 ge
、gn
和 ga
一样,具有静态存储持续时间。根据[basic.start.static]/2,它们都因此作为静态初始化的一部分进行零初始化。
但是,当试图从 S::n
和 S::a
静态数据成员。
我假设编译器是正确的,但我找不到支持这种拒绝的规范部分,或者更确切地说,为什么程序接受空 class 静态数据成员的情况 S::e
. S::b
的情况被接受是“有道理的”,但我还是无法规范地解决这个问题(odr-use,conv.lval, basic.life,...?)。
问题
- 什么规范文本解释了为什么上面的程序拒绝读取
S::e
和 S::a
的值是错误的,而它接受读取 S::e
的值(空)和S::b
(初始化)?比如说,在 N4868.
[basic.def.odr]/4 控制变量何时为 odr-used(需要定义)。
S::e
和 S::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::e
和 S::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,需要定义。
考虑以下程序:
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::a
和 S::b
两个整型静态数据成员中,后者指定了一个大括号- or-equal-initializer 在其 in-class 声明中,按照 [class.static.data]/4。 S::e
和 S::n
不能在它们的 in-class 声明中指定大括号或等于初始化器。
S
的所有这些静态数据成员,就像它们的全局命名空间作用域对应变量 ge
、gn
和 ga
一样,具有静态存储持续时间。根据[basic.start.static]/2,它们都因此作为静态初始化的一部分进行零初始化。
但是,当试图从 S::n
和 S::a
静态数据成员。
我假设编译器是正确的,但我找不到支持这种拒绝的规范部分,或者更确切地说,为什么程序接受空 class 静态数据成员的情况 S::e
. S::b
的情况被接受是“有道理的”,但我还是无法规范地解决这个问题(odr-use,conv.lval, basic.life,...?)。
问题
- 什么规范文本解释了为什么上面的程序拒绝读取
S::e
和S::a
的值是错误的,而它接受读取S::e
的值(空)和S::b
(初始化)?比如说,在 N4868.
[basic.def.odr]/4 控制变量何时为 odr-used(需要定义)。
S::e
和 S::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::e
和 S::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,需要定义。