如果我们的头文件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,在重载决议(13.3)之后,应指在D定义内定义的实体,或指同一实体, 部分模板专业化(14.8.3)匹配后,除外 一个名称可以引用一个带有internal或的非易失性const对象 如果对象在所有定义中具有相同的文字类型,则不进行链接 D,并使用常量表达式(5.19)初始化对象, 并且对象没有使用odr,并且对象具有相同的值 D 的所有定义
答案 0 :(得分:4)
我能看到任何有关您案件的问题的唯一地方是您使用foo
是否符合odr-used
的条件。或许至少澄清意图的最简单的方法是引用n1337的相应部分(紧接着官方标准,主要是清理一些措辞,例如在这种情况下):
[...]如果对象在D的所有定义中具有相同的文字类型,则该名称可以引用具有内部链接或无链接的const对象,并且该对象使用常量表达式(5.19)初始化,并且使用对象的值(但不是地址),并且对象在D的所有定义中具有相同的值;
您的使用显然符合所有这些要求。
foo
在每种情况下都有int
类型。foo
使用常量表达式进行初始化。foo
。foo
在widget
的所有定义中具有相同的值。也就是说,将foos
更改为std::vector
可能会更好:
struct widget {
std::vector<int> bars;
widget : bars(foo) {}
};
答案 1 :(得分:1)
至于foo
的多个定义,我认为foo
没有使用,因为它满足出现在常量表达式中的要求,如3.2.3所述:
变量x的名称显示为可能被评估的表达式ex,除非x是满足出现在常量表达式中的要求的对象,否则使用odr使用
因此,因为它没有使用odr规则不适用于它,3.2.4:
每个程序都应该包含每个非内联函数或odr使用的变量的一个定义 在该计划中
至于widget
的两个不同定义,以及根据3.2.6它们是否足够相似。答案是肯定的,因为N3485 3.2.6:
如果对象在D的所有定义中具有相同的文字类型[yes,both int],则名称可以引用const对象[yes,constexpr是const],内部[constexpr是内部]或没有链接使用常量表达式[yes,10]初始化object,并使用对象的值(但不是地址),并且对象在D的所有定义中具有相同的值[yes,10]
因此,即使名称foo
指的是两个不同TU中的两个不同实体,这两个实体也满足给定的要求。
(实际上它可行,因为编译器会以相同的方式布局这两个类,因此来自两个TU的生成代码将是兼容的。)
答案 2 :(得分:0)
我没有得到你的解释。引用的文本适用于引用的示例。只要你在所有TU中都包含它。要违反ODR,您必须打破文本内容:
constexpr double foo = 10; // type
constexpr int foo = ret_10(); // non-const init
constexpr int foo = 42; // value
为了打破ODR的使用,我认为你必须采取其地址。