constexpr and ODR

constexpr and ODR

本文关键字:ODR and constexpr      更新时间:2023-10-16

如果我们有一个头文件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的所有定义中具有相同的值;

您的使用显然符合所有这些要求。

  1. foo在所有情况下都具有int类型。
  2. foo用常量表达式初始化。
  3. 只使用foo的值,而不使用地址。
  4. foowidget的所有定义中具有相同的值。

也就是说,你最好将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中的两个不同实体,但这两个实体满足给定的要求。

(实际上它可以工作,因为编译器将两个类的布局相同,因此从两个tu生成的代码将是兼容的。)

我不明白你的解释。引用的文本适用于引用的例子。只要你把它像这样包含在所有的图中。要违反ODR,您必须破坏文本的某些内容:

constexpr double foo = 10; // type
constexpr int foo = ret_10(); // non-const init
constexpr int foo = 42; // value

要打破odr用法,我认为你必须取它的地址