16

该标准似乎暗示,如果不使用 odr(第 3.2/3 节),则对变量的定义数量没有限制:

每个程序都应包含该程序中 odr 使用的每个非内联函数或变量的准确定义;无需诊断。

它确实说任何变量都不能在翻译单元中多次定义(第 3.2/1 节):

任何翻译单元都不得包含一个以上的任何变量、函数、类类型、枚举类型或模板的定义。

但是我在整个程序中找不到对非 ODR 使用的变量的限制。那么为什么我不能编译如下内容:

// other.cpp
int x;

// main.cpp
int x;
int main() {}

使用 g++ 4.6.3 编译和链接这些文件,我得到一个链接器错误multiple definition of 'x'。老实说,我期待这一点,但由于x没有在任何地方使用 odr(据我所知),我看不出标准如何限制这一点。还是未定义的行为?

4

4 回答 4

11

您的程序违反了链接规则。C++11 §3.5[basic.link]/9 状态:

两个相同且在不同范围内声明的名称应表示相同的变量、函数、类型、枚举器、模板或命名空间,如果

  • 两个名称都有外部链接,否则两个名称都有内部链接并在同一个翻译单元中声明;和

  • 两个名称都指同一个命名空间的成员或同一个类的成员,而不是继承;和

  • 当两个名称都表示函数时,函数的参数类型列表是相同的;和

  • 当两个名称都表示函数模板时,签名是相同的。

(我引用了完整的段落以供参考。后两个项目符号在这里不适用。)

在您的程序中,有两个名称x相同。它们在不同的作用域中声明(在这种情况下,它们在不同的翻译单元中声明)。两个名称都有外部链接,并且两个名称都引用同一名称空间(全局名称空间)的成员。

这两个名称表示同一个变量。声明int x;定义了一个变量。因为程序中有两个这样的定义,所以程序中有两个变量。一个翻译单元中的名称“x”表示这些变量之一;另一个翻译单元中的名称“x”表示另一个。因此,该程序是格式错误的。

于 2012-10-19T17:02:18.483 回答
8

你说得对,标准在这方面有问题。我有一种感觉,这种情况属于 3.2p1(每个翻译单元最多一个定义,如您的问题)和 3.2p6(描述类、枚举、内联函数和各种模板如何在跨翻译单位)。

为了比较,在 C 中,6.9p5 要求(我强调):

外部定义是一个外部声明,它也是函数(内联定义除外)或对象的定义。如果用外部链接声明的标识符在表达式中使用(而不是作为sizeofor_Alignof运算符的操作数的一部分,其结果是整数常量),则在整个程序的某处,该标识符应该只有一个外部定义;否则,不得超过一个

于 2012-10-19T17:38:31.057 回答
1

如果标准没有说明未使用变量的定义,那么您不能暗示可能有多个:

当本国际标准省略对任何明确的行为定义的描述时,也可能会出现未定义的行为。

所以它可以很好地编译和运行,或者可能在翻译过程中停止并显示错误消息,或者可能会导致运行时崩溃等。

编辑:见 James McNellis 回答标准确实有关于它的规则。

于 2012-10-19T16:57:28.440 回答
0

编译没有错误,错误在于它的链接。默认情况下,您的全局变量或函数对其他文件(具有extern存储空间)是公共的,因此最后当链接器想要链接您的代码时,它会看到两个定义x并且它无法选择其中一个,所以如果您不x使用main.cppinother.cpp反之亦然使它们成为静态的(这意味着仅对包含它的文件可见)

// other.cpp
static int x;

// main.cpp
static int x;
于 2012-10-19T16:52:48.257 回答