constexpr和ODR

时间:2013-06-17 00:26:41

标签: c++ c++11 constexpr one-definition-rule

如果我们的头文件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

的所有定义

3 个答案:

答案 0 :(得分:4)

我能看到任何有关您案件的问题的唯一地方是您使用foo是否符合odr-used的条件。或许至少澄清意图的最简单的方法是引用n1337的相应部分(紧接着官方标准,主要是清理一些措辞,例如在这种情况下):

  

[...]如果对象在D的所有定义中具有相同的文字类型,则该名称可以引用具有内部链接或无链接的const对象,并且该对象使用常量表达式(5.19)初始化,并且使用对象的值(但不是地址),并且对象在D的所有定义中具有相同的值;

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

  1. foo在每种情况下都有int类型。
  2. foo使用常量表达式进行初始化。
  3. 您只使用foo
  4. 的值,而不是地址
  5. foowidget的所有定义中具有相同的值。
  6. 也就是说,将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的使用,我认为你必须采取其地址。