constexpr global of class type

1.2k views Asked by At

My understanding is that constexpr globals of class type are all but unusable because

  • Such an object must be defined in every TU, because constexpr does not permit forward declaration of an object.

  • Default linkage as static would cause naming the object (ODR-use or not) in an inline function to violate the ODR, because the respective inline definitions would have different meaning.

  • Declaration as extern constexpr with one definition per TU would violate the ODR rule if the object is ODR-used, which occurs when a reference to it is taken.

    • A reference is taken for an implicit this parameter, even if it's unused by a member function.
    • Obviously happens if you try to pass the object by reference.
    • Also happens if you try to pass the object by value, which implicitly uses a copy or move constructor, which by definition passes by reference.
    • GCC and Clang both complain of ODR violations (multiple definitions) if an object is declared extern constexpr even if not ODR-used.

Is this all correct? Is there any way to have a constexpr global of class type without wrapping it in an inline function?

1

There are 1 answers

10
TemplateRex On

Global constexpr variables can ODR-safely be defined in headers using a bit of macro magic and the proverbial extra level of indirection

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

The macro provides a reference inside an unnamed namespace to an object instance in an implementation class template.

Each object in an unnamed namespace inside a header generates a unique instance in every translation unit that includes its header. Furthermore, to prevent ODR violations, it is important that the objects in e.g. multiple instantiations of a function template are the same.

However, for references it doesn't matter that they have a different identity; as long as they refer to the same object instance in an implementation class template.

You can wrap this macro in a header and safely include it in many TUs without a problem.

See the following discussion on the Boost mailinglist for more details: http://lists.boost.org/Archives/boost/2007/06/123380.php