【问题标题】:constexpr global constants in a header file and odr头文件和 odr 中的 constexpr 全局常量
【发布时间】:2016-03-30 11:00:21
【问题描述】:

不幸的是,我对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";

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

【问题讨论】:

  • @JohnB:您可能希望将其标记为 constexpr char const * const kHello = "Hello"; ——在 c++11 中,constexpr 意味着 const,但在 c++14 中却没有。我不确定在这个问题上 C++14 中的 ODR 是否有很大不同,但无论如何,根据我的经验,你可以得到一个编译器警告。
  • @ChrisBeck 你在成员函数上混淆了constexpr,在变量上混淆了constexpr
  • 这只是一个学术问题。实际上不会发生任何错误,因为(对于未内联对 inline 函数的调用的情况)链接器只是在可丢弃的众多中选择一个任意(内联)函数定义,而单个 确实你想要什么。除非您有意引入对常量地址或此类愚蠢的依赖。也就是说,没有实际问题。
  • @Cheersandhth.-Alf 转发功能模板。是的,有,因为这将构成 odr 使用。 (如果你在内部进行,例如标题中的内联函数。)
  • @Columbo: 好吧,无论如何 :) 圣诞快乐。又是那个时候!哎呀。 OK,开始准备……

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


【解决方案1】:

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


TL;DR:如果您想(非常)安全,请使用 constexpr 函数。但是,它本质上并不是必需的,如果您对这些对象执行微不足道的操作并且只对它们的价值感兴趣,或者根本不在下面列出的危险场景中使用它们,那么肯定不会是必需的。

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

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

“使用常量表达式初始化” 肯定会遇到,因为我们在谈论 constexpr“如果你不胡思乱想的话,“对象在D的所有定义中都具有相同的值”

“该对象没有被 odr-used” 可能是唯一有问题的条件。基本上,它要求您不必将变量运行时作为符号存在,这反过来意味着

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

  • 您不会(无论是明确的还是隐含的)获取它的地址。

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

更准确地说,odr 使用由[basic.def.odr]/3 管理:

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

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

如果您避免在内联函数、模板等中对变量进行 odr 使用,那就没问题了。但是,如果您使用相应的 constexpr 函数的返回值,则不必担心,因为纯右值已经表现得更像值/文字(不是对象),并且 constexpr 函数是内联的并且绝对不会违反 ODR(如果你不要在里面使用constexpr变量!)。

【讨论】:

  • "您基本上已经完成了使用相应 constexpr 函数的返回值无法完成的操作," 并非如此。引用绑定(转发引用或 const 左值引用)确实是主要问题。
  • 因此,如果我理解正确的话,我假设如果我使用“函数返回值”方法我必须少思考是正确的吗?我的问题是:我想我理解这个问题,但我感觉不够流利,无法避免意外错误。
  • @T.C.好吧,准确地说,该操作将是“将引用绑定到左值”与“´´ 到右值”,尽管我同意这还不清楚 :o)
  • @JohnB 好吧,如果你相当小心(即当你将它引入内联函数或类似函数时迅速检查每一次使用),应该不会出错。如果您根本不想关心这个,请使用constexpr 函数。这最终是你的决定。
  • 我不太明白你认为 [basic.def.odr]p2.2 有什么缺陷。你看过stackoverflow.com/a/23436665 和下面的讨论吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-27
  • 1970-01-01
  • 2014-12-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多