constexpr类的全球类型

Pot*_*ter 15 c++ global one-definition-rule constexpr c++11

我的理解是constexpr类类型的全局变量几乎无法使用,因为

  • 必须在每个TU中定义这样的对象,因为constexpr不允许对象的前向声明.

  • 默认链接static会导致在内联函数中命名对象(ODR使用与否)以违反ODR,因为相应的inline定义具有不同的含义.

  • extern constexpr如果对象是ODR使用的,则每个TU的一个定义的声明将违反ODR规则,这在对其进行引用时发生.

    • 引用隐式this参数,即使它已被成员函数使用.
    • 如果您尝试通过引用传递对象,显然会发生这种情况.
    • 如果您尝试按值传递对象也会发生这种情况,该值隐式使用副本或移动构造函数,根据定义,它通过引用传递.
    • 如果extern constexpr即使没有使用ODR,也会声明对象,GCC和Clang都会抱怨ODR违规(多个定义).

这都是正确的吗?有没有办法让一个constexpr全局类类型没有包装在一个inline函数中?

Tem*_*Rex 1

全局 constexpr 变量可以使用一些宏魔法和众所周知的额外间接级别以 ODR 方式安全地在标头中定义

#define PP_GLOBAL_CONSTEXPR_VARIABLE(type, var, value)                   \
namespace var##detail {                                                  \
template<class = void>                                                   \
struct wrapper                                                           \
{                                                                        \
     static constexpr type var = value;                                  \
};                                                                       \
template<class T>                                                        \
constexpr type wrapper<T>::var;                                          \
}                                                                        \
namespace {                                                              \
auto const& var = var##detail::wrapper<>::var;                           \
}
Run Code Online (Sandbox Code Playgroud)

该宏在未命名的命名空间内提供对实现类模板中的对象实例的引用。

标头内未命名命名空间中的每个对象都会在包含其标头的每个翻译单元中生成一个唯一的实例。此外,为了防止 ODR 违规,重要的是函数模板的多个实例中的对象是相同的。

然而,对于参考文献来说,它们具有不同的身份并不重要;只要它们在实现类模板中引用相同的对象实例即可。

您可以将此宏包装在标头中,并将其安全地包含在许多 TU 中,不会出现任何问题。

有关更多详细信息,请参阅以下有关 Boost 邮件列表的讨论: http://lists.boost.org/Archives/boost/2007/06/123380.php