15

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

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

  • 默认链接static会导致在内联函数中命名对象(使用或不使用 ODR)违反 ODR,因为各自的inline定义将具有不同的含义。

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

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

这一切都正确吗?有没有办法在constexpr不将其包装在函数中的情况下拥有一个全局类类型inline

4

1 回答 1

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;                           \
}

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

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

但是,对于引用,它们具有不同的身份并不重要。只要它们在实现类模板中引用相同的对象实例。

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

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

于 2013-12-04T11:44:11.773 回答