constexpr头文件中的全局常量和odr

时间:2015-12-23 23:35:25

标签: c++ c++11 language-lawyer constexpr linkage

不幸的是,我对constexpr,头文件中声明的全局常量以及odr感到有些困惑。

简而言之:我们可以从这里结束

https://isocpp.org/files/papers/n4147.pdf

constexpr MyClass const MyClassObj () { return MyClass {}; }
constexpr char const * Hello () { return "Hello"; }

优于

constexpr MyClass const kMyClassObj = MyClass {};
constexpr char const * kHello = "Hello";

用于在头文件中定义全局变量 如果我想"只需使用"那些全局声明/定义的实体,并且不想考虑 我如何使用它们?

1 个答案:

答案 0 :(得分:16)

注意:从C ++ 17开始,您可以声明variables as inline

TL; DR :如果您想要(非常)安全,请使用constexpr功能。它本身并不是必需的,如果你对这些对象进行琐碎的操作并且只关心它们的价值,或者根本不在下面列出的危险场景中使用它们,那肯定不是必需的。

根本问题是命名空间范围内的const变量(例如您的)通常具有内部链接([basic.link]/(3.2))。这意味着每个编译相应标题的翻译单元将观察到不同的实体(即符号)。

现在假设我们在使用这些对象的标题中有一个模板或内联函数。 ODR对这种情况非常精确 - [basic.def.odr]/6

enter image description here

因为我们正在谈论constexpr,所以肯定会遇到

“用常量表达式初始化”。所以“如果你没有喋喋不休,那么D 的所有定义中的对象都具有相同的价值。

“对象没有使用”可能是唯一可疑的条件。基本上,它要求您不必将变量运行时存在作为符号,这反过来暗示

  • 您不会将其绑定到引用(=>您不转发它!)

  • 您(不是明确地或隐含地)不接受其地址。

第二个规则的唯一例外是数组,只要上述两个规则没有违反产生的glvalue,就可以在下标操作中隐式获取地址。

更准确地说,odr-use受[basic.def.odr]/3

的支配
  

x的名称显示为可能评估的表达式ex的变量exx使用,除非应用   左值到右值的转换(4.1)到x产生一个不调用任何非平凡函数的常量表达式(5.20),如果ex是一个对象,e是表达式e的潜在结果集合的元素,其中左值到右值转换(4.1)应用于e,或constexpr是丢弃值表达式(条款   5)。

将l-t-r应用于任何constexpr变量将按第一部分的要求运行。第二部分要求变量用作而不是实际的对象;也就是说,它最终被丢弃或直接评估,给出了上述经验法则。

如果你避免在内联函数,模板等内部使用变量,你就没问题了。但是如果使用相应constexpr函数的返回值,则不必担心,因为prvalues已经表现得更像值/文字(而不是对象),constexpr函数是内联的,绝对不会违反ODR(如果你不在里面使用moreSymptomsView变量!)。