如果我们有一个头文件widget.hpp
,其内容如下:
constexpr int foo = 10;
struct widget
{
int bars[foo];
};
…并且我们从两个源文件生成了两个翻译单元,它们都只包含widget.hpp
,这是否违反了一个定义规则(更具体地说,foo
的使用是否违反了一个定义规则)?
foo
具有内在联系,但也是一个常数表达式。根据我对c++ 11标准3.2.6的阅读(我将在下面引用),如果需求#2不是仅仅指静态数据成员,那么这是格式良好的。
3.2.6要求#2:
在每个定义D,对应的名称,查找根据3.4,指在D定义内定义的实体,或指在重载解析(13.3)和之后定义的同一实体在匹配部分模板特化(14.8.3)之后,except一个名称可以引用具有内部或的非易失性const对象如果对象在所有定义中具有相同的文字类型,则没有链接,对象用常量表达式(5.19)初始化,并且对象不是odr使用的,对象在D
的所有定义
关于您的案例,我可以看到任何问题的唯一地方是您对foo
的使用是否符合odr-used
。也许最简单的方法来澄清至少意图是引用n1337的相应部分(立即遵循官方标准,主要清理一些措辞,如在这种情况下):
[…]一个名称可以引用具有内部链接或没有链接的const对象,如果该对象在D的所有定义中具有相同的文字类型,并且该对象使用常量表达式(5.19)初始化,并且使用了该对象的值(但不是地址),并且该对象在D的所有定义中具有相同的值;
您的使用显然符合所有这些要求。
-
foo
在所有情况下都具有int
类型。 -
foo
用常量表达式初始化。 - 只使用
foo
的值,而不使用地址。 -
foo
在widget
的所有定义中具有相同的值。
也就是说,你最好将foos
改为std::vector
:
struct widget {
std::vector<int> bars;
widget : bars(foo) {}
};
对于foo
的多重定义,我不认为foo
是odr使用的,因为它满足了出现在常量表达式中的要求,如3.2.3:
如果变量x的名字出现在可能求值的表达式ex中,则不允许使用,除非x是满足出现在常量表达式
中的对象。
因此,由于它不是odr使用的,所以odr规则对它不适用,3.2.4:
每个程序应该只包含一个非内联函数或变量的定义在那个程序
对于widget
的两种不同定义,根据3.2.6是否足够相似。答案是肯定的,因为N3485 3.2.6:
如果对象在D的所有定义中具有相同的文字类型[yes,都是int],并且使用常量表达式[yes, 10]初始化,并且使用了对象的值(但不是地址),并且该对象在D的所有定义中具有相同的值[yes, 10],则名称可以引用带有内部[constexpr is internal]的const对象[yes, constexpr is const],或者没有链接,
因此,尽管名称foo
指的是两个不同tu中的两个不同实体,但这两个实体满足给定的要求。
我不明白你的解释。引用的文本适用于引用的例子。只要你把它像这样包含在所有的图中。要违反ODR,您必须破坏文本的某些内容:
constexpr double foo = 10; // type
constexpr int foo = ret_10(); // non-const init
constexpr int foo = 42; // value
要打破odr用法,我认为你必须取它的地址