不幸的是,我有点困惑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";
如果我想"只使用"那些全局声明/定义的实体并且不想考虑我如何使用它们,那么在头文件中定义全局变量?
注意:从C++ 17开始,您可以将变量声明为内联.
TL; DR:如果您想要(非常)安全,请使用constexpr功能.它本身并不是必需的,如果你对这些对象进行琐碎的操作并且只关心它们的价值,或者根本不在下面列出的危险场景中使用它们,那肯定不是必需的.
基本问题是const
命名空间范围内的变量(如您的)(通常)具有内部链接([basic.link] /(3.2)).这意味着编译相应头部的每个翻译单元将观察不同的实体(即符号).
现在假设我们在使用这些对象的头文件中有一个模板或内联函数.ODR对这种情况非常精确 - [basic.def.odr]/6:
因为我们正在谈论,"以常数表达式初始化"肯定得到满足constexpr
.所以"如果你不喋喋不休,那么对象在所有定义中都具有相同的价值D
".
"对象没有使用"可能是唯一可疑的条件.基本上,它要求您不必将变量运行时存在作为符号,这反过来暗示了这一点
你没有将它绑定到引用(=>你不转发它!)
你没有(既不明确也不含蓄地)取其地址.
第二个规则的唯一例外是数组,只要上述两个规则没有违反产生的glvalue,就可以在下标操作中隐式获取地址.
更确切地说,odr-use由[basic.def.odr]/3管理:
除非应用左值到右值转换(4.1)以产生不调用任何非平凡函数的常量表达式(5.20),否则
x
其名称显示为可能已评估的表达式的变量将被ex
使用.如果是object,是表达式的潜在结果集合的元素,其中左值到右值的转换(4.1)应用于或者是丢弃值表达式(第5节).ex
x
x
ex
e
e
e
将ltr应用于任何constexpr
变量将按照第一部分的要求运行.第二部分要求变量用作值而不是实际对象 ; 也就是说,它最终被丢弃或直接评估,给出了上述经验法则.
如果你避免在内联函数,模板等内部使用变量,你就可以了.但是如果使用相应constexpr函数的返回值,则不必担心,因为prvalues已经表现得更像值/文字(而不是对象),constexpr函数是内联的,绝对不会违反ODR(如果你不在constexpr
里面使用变量!).