Reputation: 137910
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.
this
parameter, even if it's unused by a member function.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?
Upvotes: 15
Views: 1240
Reputation: 70556
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
Upvotes: 1