为什么不是';获取内联定义的静态常量整型成员变量的地址是否违反了ODR? P>这样的东西如果编译为:,将明显违反C++一个定义规则。 // Case 1 // something.h struct S {}; struct A { static const S val = S(); };

为什么不是';获取内联定义的静态常量整型成员变量的地址是否违反了ODR? P>这样的东西如果编译为:,将明显违反C++一个定义规则。 // Case 1 // something.h struct S {}; struct A { static const S val = S(); };,c++,static-members,language-lawyer,one-definition-rule,C++,Static Members,Language Lawyer,One Definition Rule,因为如果某个.h包含在多个模块中,则会重复A::val的定义。但是,这是允许的: // Case 2 // someOtherThing.h struct B { static const int val = 3; }; 据我所知,这种情况之所以可以,是因为B::val是一个整数类型的编译时常量,因此编译器基本上可以搜索并用一个文本3替换对B::val的所有引用(对反汇编的检查表明这正是它所做的)。因此,在某种意义上,最终产品中对B::val的定义为零,因此ODR不适用。但是,

因为如果某个.h包含在多个模块中,则会重复
A::val
的定义。但是,这是允许的:

// Case 2    
// someOtherThing.h

struct B
{
   static const int val = 3;
};
据我所知,这种情况之所以可以,是因为
B::val
是一个整数类型的编译时常量,因此编译器基本上可以搜索并用一个文本
3
替换对
B::val
的所有引用(对反汇编的检查表明这正是它所做的)。因此,在某种意义上,最终产品中对
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\u大象
关键字的行为都会导致数字42的每一个实例都被666替换,那么这就是问题所在,但我们仍然想知道为什么会存在这样一个奇怪的规则。)您的第一个示例没有违反ODR,因为 类内静态成员的声明不正确 一个定义,只是一个声明。静态成员必须是 在单个翻译单元中定义,例如:

S const A::val;
在源文件(不是头文件)中

在C++11之前的版本中,当声明是静态的时,具有整数类型 而且是常量,允许(作为特殊例外) 指定初始值设定项,前提是初始值设定项为常量 积分表达式。然而,即使在这种情况下,您也可以 需要一个定义(不带初始值设定项),并且只需要 一个源文件。如果缺少,则结果未定义 行为。(IIFC,有一个例外:如果变量 用于需要整数常量表达式的上下文中, 不需要定义。)

我认为C++11已经扩展了一些,并且允许一些 非整数类型也可以在类中具有初始值设定项 定义。但它仍然需要定义的范围之外 班级

关于你声称有效的最后一个例子:它是 在C++11之前的版本中是不合法的,并且它会在许多应用程序中导致错误 编译器。(我的印象是C++11成功了 合法,并让编译器生成实例, 但是我现在找不到合适的词。如果它是制作的 在C++11中是合法的,那么这只是VC++2012使用的C++11特性

你的问题是什么?为什么标准允许合理和方便的事情?因为它们是合理和方便的。@n.m.我会质疑#3是否合理,因为它可能需要编译器跳过一系列的限制,以使其工作,以换取(IMO)非常小的方便。但如果是的话,那么#1当然也是合理和方便的,但它是被禁止的。它不起作用。如果使用非内联函数F(因此不会删除整个函数),您会注意到(使用gcc时)符号_ZN1C3valE未定义。编译器已经为模板类的静态成员执行了这项工作,因此删除此功能并不会真正为编译器编写器节省任何工作。这与案例1的区别在于,在案例1中,编译器无法证明
val
的多个定义是相同的。我误解了你的问题。但是,此处不禁止任何内容,“如果使用odr,则仍应在名称空间范围中定义该成员”(9.4.2/3)。因此,如果你还没有做到这一点,那么微软编译器会让你逍遥法外;关于案例1,你是对的;我编辑它以包含一个赋值,因为这正是我想要的。@dlf c++11允许使用constexpr替换const的情况1。无论是否合法,这都不是违反ODR的行为,因为这里没有定义,只是一个声明,即使有初始值设定项。@n.m.我(错误地)担心案例二和案例三涉及在类定义中定位成员的定义。事实上,正如您和James Kanze所明确指出的,所有发生的事情都是初始化被定位在仍然只是一个声明的位置上。这个认识和你在问题下面引用的标准部分为我澄清了一切。@JamesKanze:根据n.m.在问题下面引用的C++11草案的9.4.2/3,你在最后一段中的附加注释似乎不正确。除非在草稿之后更改?@dlf,否则您的任何示例中都没有赋值;在任何情况下,分配操作员都是合法的。所有这些都只是指定静态数据成员的初始化。