头文件和 odr 中的 constexpr 全局常量
constexpr global constants in a header file and odr
不幸的是,我对 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";
用于在头文件中定义全局变量
如果我想要 "just use" 那些 globally declared/defined 实体并且不想考虑 如何 我使用它们?
注意:从 C++17 开始,您可以声明 variables as inline。
TL;DR:如果您想(非常)安全,请使用 constexpr 函数。不过,这并不是本质上必需的,如果您对这些 object 执行微不足道的操作并且只对它们的价值感兴趣,或者根本不在下面列出的危险场景中使用它们,那么肯定不会.
根本问题是 const
命名空间范围内的变量(通常)具有内部链接 ([basic.link]/(3.2))。这意味着 每个编译相应 header 的翻译单元将观察到不同的实体(即符号)。
现在假设我们在 header 中使用那些 object 有一个模板或内联函数。 ODR 对这种情况非常准确 - [basic.def.odr]/6:
"initialized with a constant expression"肯定满足了,既然我们说的是constexpr
。 "the object has the same value in all definitions of D
" 如果你不胡闹也是如此。
"the object isn't odr-used" 可能是唯一有问题的条件。基本上,它要求您不必将变量运行时存在作为符号,这反过来意味着
您没有将它绑定到引用(=> 您没有转发它!)
你不(既不明确也不隐含地)获取它的地址。
第二条规则的唯一例外是数组,它可以在下标操作中隐式地取地址,只要不违反以上两条规则产生的泛左值。
更准确地说,odr-use 受制于 [basic.def.odr]/3:
A variable x
whose name appears as a potentially-evaluated expression ex
is odr-used by ex
unless applying
the lvalue-to-rvalue conversion (4.1) to x
yields a constant expression (5.20) that does not invoke any non-trivial functions and, if x
is an object, ex
is an element of the set of potential results of an expression e
, where either the lvalue-to-rvalue conversion (4.1) is applied to e
, or e
is a discarded-value expression (Clause
5).
将 l-t-r 应用于任何 constexpr
变量将按照第一部分的要求运行。第二部分要求将变量用作 value 而不是实际的 object;也就是说,它最终要么被丢弃,要么被直接评估,给出上述经验法则。
如果在内联函数、模板等内部避免使用 odr-use 变量,就可以了。但是如果你使用相应的 constexpr 函数的 return 值,你就不必担心,因为纯右值已经表现得更像 values/literals(而不是 objects)并且 constexpr 函数是内联并且绝对不会违反 ODR(如果你不在其中使用 constexpr
变量!)。
不幸的是,我对 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";
用于在头文件中定义全局变量 如果我想要 "just use" 那些 globally declared/defined 实体并且不想考虑 如何 我使用它们?
注意:从 C++17 开始,您可以声明 variables as inline。
TL;DR:如果您想(非常)安全,请使用 constexpr 函数。不过,这并不是本质上必需的,如果您对这些 object 执行微不足道的操作并且只对它们的价值感兴趣,或者根本不在下面列出的危险场景中使用它们,那么肯定不会.
根本问题是 const
命名空间范围内的变量(通常)具有内部链接 ([basic.link]/(3.2))。这意味着 每个编译相应 header 的翻译单元将观察到不同的实体(即符号)。
现在假设我们在 header 中使用那些 object 有一个模板或内联函数。 ODR 对这种情况非常准确 - [basic.def.odr]/6:
"initialized with a constant expression"肯定满足了,既然我们说的是constexpr
。 "the object has the same value in all definitions of D
" 如果你不胡闹也是如此。
"the object isn't odr-used" 可能是唯一有问题的条件。基本上,它要求您不必将变量运行时存在作为符号,这反过来意味着
您没有将它绑定到引用(=> 您没有转发它!)
你不(既不明确也不隐含地)获取它的地址。
第二条规则的唯一例外是数组,它可以在下标操作中隐式地取地址,只要不违反以上两条规则产生的泛左值。
更准确地说,odr-use 受制于 [basic.def.odr]/3:
A variable
x
whose name appears as a potentially-evaluated expressionex
is odr-used byex
unless applying the lvalue-to-rvalue conversion (4.1) tox
yields a constant expression (5.20) that does not invoke any non-trivial functions and, ifx
is an object,ex
is an element of the set of potential results of an expressione
, where either the lvalue-to-rvalue conversion (4.1) is applied toe
, ore
is a discarded-value expression (Clause 5).
将 l-t-r 应用于任何 constexpr
变量将按照第一部分的要求运行。第二部分要求将变量用作 value 而不是实际的 object;也就是说,它最终要么被丢弃,要么被直接评估,给出上述经验法则。
如果在内联函数、模板等内部避免使用 odr-use 变量,就可以了。但是如果你使用相应的 constexpr 函数的 return 值,你就不必担心,因为纯右值已经表现得更像 values/literals(而不是 objects)并且 constexpr 函数是内联并且绝对不会违反 ODR(如果你不在其中使用 constexpr
变量!)。