读取没有定义的静态常量数据成员的值:这些规则受什么约束?
考虑以下程序:
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::a
和 S:: b
后者在其类内声明中指定了一个大括号或等于初始化器,根据 [class.static.data]/4。 S::e
和 S::n
不得在其类内声明中指定括号或等于初始化器:s。
S
的所有这些静态数据成员都像它们的全局命名空间范围对应变量一样具有 ge
、gn
和 ga
,静态存储时间。根据 [basic.start.static]/2 因此,作为静态初始化的一部分,它们都经历零初始化。
然而,当尝试复制初始化局部变量时,上面的程序在 #1
和 #2
被 GCC 和 Clang(各种语言版本、各种编译器版本)拒绝来自 S::n
和 S::a
静态数据成员。
我假设编译器是正确的,但我找不到支持这种拒绝的规范部分,或者更确切地说,为什么程序接受空类静态数据成员 S::e
的情况。 S::b
的情况被接受“有道理”,但我再次无法规范地解决这个问题(odr-use,conv.lval, basic.life,...?)。
问题
- 什么规范文本解释了为什么上面的程序拒绝读取
S::e
和S::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
andS::a
as ill-formed, whilst it accepts read the values ofS::e
(empty) andS::b
(initialized)? Say, in N4868.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
[basic.def.odr]/4 管辖当变量被 odr 使用时(需要定义)。
S::e
和S::n
不符合例外 (4.1) 的条件,因为它们具有非引用类型。它们不符合例外 (4.2) 的条件,因为它们不能 在常量中使用表达式 因为它们不能是 潜在恒定,并且它们也不符合“应用左值到右值转换的非易失性限定非类类型”标准。它们不符合例外 (4.3) 的条件,因为它们也没有被丢弃;它们绑定到复制构造函数的引用参数。这意味着
S::e
和S::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
andS::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
andS::n
are odr-used. You only got a diagnostic forS::n
, notS::e
. This does not imply that your use ofS::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 ofEmpty
, and generated object files where the linker has no way to know thatS::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 toS::b
is "converted" into the value ofS::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.