【发布时间】:2014-05-27 13:54:57
【问题描述】:
这样的事情如果编译的话,显然违反了 C++ 的单一定义规则:
// Case 1
// something.h
struct S {};
struct A
{
static const S val = S();
};
因为如果 something.h 包含在多个模块中,A::val 的定义将被重复。但是,这是允许的:
// Case 2
// someOtherThing.h
struct B
{
static const int val = 3;
};
据我了解,这种情况正常的原因是因为B::val 是一个整数类型的编译时常量,所以编译器基本上可以对所有对B::val 的引用进行搜索和替换文字3(反汇编的检查表明这正是它所做的)。因此,在某种意义上,在最终产品中,B::val 的定义零,因此 ODR 不适用。但是,请考虑一下:
// Case 3
// yetAnotherThing.h
struct C
{
static const int val = 3;
const int* F()
{
return &val;
}
};
这是允许的,反汇编显示在这种情况下,实际上已经预留了一些内存位置来存储C::val的值。从表面上看,这意味着如果 yetAnotherThing.h 包含在多个模块中,我们现在违反了 ODR,因为 static const int val = 3 现在会导致存储被“发射”。然而编译器和链接器 (VC++2012) 都没有抱怨。
为什么?这只是编译器/链接器的作者必须处理的令人讨厌的特殊情况吗?如果是这样,为什么不能使用相同的系统来使案例 #1 也可以工作?
(欢迎使用标准中的相关引用,但它们不一定能回答问题。如果标准说pink_elephants 关键字的任何使用都应该导致数字 42 的每个实例都被替换为 666,那么就是这样,但我们仍然想知道为什么存在这样一个奇怪的规则。)
【问题讨论】:
-
您的问题是什么?为什么标准允许合理方便的事情?因为它们既合理又方便。
-
@n.m.我会质疑#3是否合理,因为这可能需要编译器跳过一堆箍以使其工作以换取(IMO)非常小的便利。但如果是的话,那么#1当然也是合理方便的,但它是被禁止的。
-
它不起作用。如果您使用非内联函数 F(因此不会删除整个内容),您会注意到(使用 gcc)符号 _ZN1C3valE 未定义。
-
编译器已经必须为模板类的静态成员完成这项工作,因此删除此功能并不能真正为编译器编写者节省任何工作。这与案例#1 的区别在于,在案例#1 中,编译器无法证明
val的多个定义是相同的。 -
我误解了你的问题。但是,这里没有禁止任何内容,“如果成员被 odr-used,则仍应在命名空间范围内定义”(9.4.2/3)。因此,如果您还没有这样做,那么 Microsoft 编译器会让您侥幸逃脱。
标签: c++ static-members language-lawyer one-definition-rule