非静态数据成员和一个定义规则
Posted
技术标签:
【中文标题】非静态数据成员和一个定义规则【英文标题】:non-static data members and one definition rule 【发布时间】:2017-04-19 03:39:43 【问题描述】:前提
按照一个定义规则,如C++14 Standard中所说,只要我遵循3.2.6中的规则,我就可以在每个翻译单元中定义同一个类.这意味着允许以下程序是合法的:
//a_1.cpp
class A //definition of A
int a; //definition of A::a
static int b; //declaration of A::b
int foo(); //declaration of A::foo();
int boo() return 42; ; //definition of A::boo() implicity inlined
;
//a_2.cpp
class A //definition of A
int a; //definition of A::a
static int b; //declaration of A::b
int foo(); //declaration of A::foo();
int boo() return 42; ; //definition of A::boo() implicity inlined
;
如果我尝试定义b
或foo()
,那么我将被限制在整个程序中的单一定义,我认为这是由于3.2.4 中的声明:
每个程序都应该包含一个对每个非内联函数或 odr 使用的变量的定义 在该计划中;无需诊断。
因此,以下程序是非良构的:
//a_1.cpp
class A //definition of A
int a; //definition of A::a
static int b; //declaration of A::b
int foo(); //declaration of A::foo();
int boo() return 42; ; //definition of A::boo() implicity inlined
;
int A::b;
//a_2.cpp
class A //definition of A
int a; //definition of A::a
static int b; //declaration of A::b
int foo(); //declaration of A::foo();
int boo() return 42; ; //definition of A::boo() implicitly inlined
;
int A::b;
如果我尝试在两个源文件中定义 foo()
也是一样的。
但是,我可以对 boo()
有多个定义(每个翻译单元一个),因为 3.2.4 并未禁止,实际上 3.2.6 明确允许这样做:
类类型(第 9 条)、枚举类型(7.2)、内联函数可以有多个定义 外部链接(7.1.2),类模板(第14条),非静态函数模板(14.5.6),静态数据成员 类模板 (14.5.1.3) 的成员函数、类模板的成员函数 (14.5.1.1) 或模板特化 在程序中未指定某些模板参数(14.7、14.5.5),前提是每个定义 出现在不同的翻译单元中。
公平地说,3.2.6 对上述语句进行了限定,增加了一些要求,其中实体(在我们的例子中为 boo()
)必须在每个翻译单元中使用相同的标记序列来定义。
问题
非静态数据成员a
呢? a
的多个定义显然是允许的(否则我问题顶部的程序将无法编译),但这似乎被 3.2.4 禁止并且不被 3.2.6 宽恕。这只是标准中没有严格规定的细节还是我遗漏了什么?
编辑
对于那些告诉我a
没有定义,而只是声明的人,请考虑这个例子,直接取自C++14 Standard,3.2.2:
struct X // defines X
int x; // defines non-static data member x
static int y; // declares static data member y
X(): x(0) // defines a constructor of X
;
请注意,上面代码的 cmets 不是我的,而是直接从标准中复制的。
【问题讨论】:
“a
的多个定义显然是允许的(否则我的问题顶部的程序将无法编译)” - 大多数 ODR 违规是“未定义的行为,不需要诊断”,因此缺乏编译错误不是被允许的证据
嗯,那是真的......我实际上对规则中的(明显的)矛盾更加困扰。多亏了答案,我现在明白了我错过了什么。无论如何,感谢您强调这个细节。
【参考方案1】:
您在第一个程序中没有a
的多个定义。您有多个a
的声明。巨大的差异。
如果您不能有多个声明,那么 include
将无法工作,因为预处理器只会在使用它的每个翻译中复制该信息。
【讨论】:
每个非静态数据成员的声明都是一个定义,根据 [basic.def]/2。【参考方案2】:[basic.def.odr]/1:
任何翻译单元不得包含任何变量、函数、类类型、枚举类型或模板的多个定义。
变量由[basic]/6定义:
变量由非静态数据成员或对象以外的引用的声明引入。
因此,由于非静态数据成员不是变量、函数、类、枚举或模板,单一定义规则根本不适用于非静态数据成员。
【讨论】:
太好了,谢谢伙计,我错过了 [basic]/6。你无法理解它对我的影响有多大!现在我终于可以放松了……顺便问一下,有没有类似于 ODR 的与非静态数据成员有关的规则?有什么需要注意的(还是涉及到多重定义的问题)? 我认为没有。类定义的主体是唯一可以定义非静态数据成员的地方,因此它已经被规则所涵盖,即类的每个定义都必须是令牌对令牌相同的。 它说“非静态数据成员以外的引用”。不是引用类型的非静态数据成员似乎没有被排除,除非它们在这种情况下不是“对象”? @JMC 我的解释是在这种情况下它们不是对象,因为它们的定义实际上并没有创建对象。只有在定义了封闭类类型的对象时,才会创建成员对象。但你可能是对的。标准措辞可能会从这里收紧。以上是关于非静态数据成员和一个定义规则的主要内容,如果未能解决你的问题,请参考以下文章