非静态数据成员和一个定义规则

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 
;

如果我尝试定义bfoo(),那么我将被限制在整个程序中的单一定义,我认为这是由于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 我的解释是在这种情况下它们不是对象,因为它们的定义实际上并没有创建对象。只有在定义了封闭类类型的对象时,才会创建成员对象。但你可能是对的。标准措辞可能会从这里收紧。

以上是关于非静态数据成员和一个定义规则的主要内容,如果未能解决你的问题,请参考以下文章

静态成员和非静态成员的区别?

在非限定 id 之后的静态数据成员定义中使用的名称

5.7 内部类

Java 静态成员类 非静态的成员类 局部类 匿名类

在 Qt 中无效使用非静态数据成员 Ui_Mainwindow::(Pushbutton)

静态和非静态的区别。