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