Constexpr global типа класса

Я понимаю, что глобальные типы constexpr типа класса являются неприменимыми, потому что

  • Такой объект должен быть определен в каждом TU, потому что constexpr не разрешает прямое объявление объекта.

  • Связь по умолчанию как static приведет к присвоению имени (ODR-использование или нет) встроенной функции для нарушения ODR, потому что соответствующие определения inline будут иметь разное значение.

  • Объявление как extern constexpr с одним определением на TU будет нарушать правило ODR, если объект используется ODR, что происходит, когда делается ссылка на него.

    • Используется ссылка для неявного параметра this, даже если он не используется функцией-членом.
    • Очевидно, что если вы попытаетесь передать объект по ссылке.
    • Также случается, если вы попытаетесь передать объект по значению, которое неявно использует конструктор копирования или перемещения, который по определению проходит по ссылке.
    • GCC и Clang жалуются на нарушения ODR (несколько определений), если объект объявлен extern constexpr, даже если он не используется ODR.

Это все правильно? Есть ли способ иметь глобальный тип constexpr типа класса без его переноса в функцию inline?

Ответы

Ответ 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