Why does cppreference define type_traits xxx_v shortcuts as inline constexpr
and not just constexpr
?
For example, see is_integral_v
:
template< class T >
inline constexpr bool is_integral_v = is_integral<T>::value;
Is this just a matter of style or is there some difference in behavior? As far as I know constexpr
variables are implicitly inline
.
Edit: Looking at the draft of the latest standard, it also uses inline constexpr
. The question actually applies to the standard, then.
[basic.link]/3.2 applies to names of "a non-inline variable of non-volatile const-qualified type". A variable template isn't a variable, just like a class template isn't a class, a function template isn't a function, and a cookie cutter isn't a cookie. So that rule doesn't apply to the variable template is_integral_v
itself.
A variable template specialization is a variable, however, so there are some questions about whether that rule gives it internal linkage. This is core issue 1713, the direction of which is that the rule is not applicable.
Core issue 1713, however, wasn't resolved in time for C++17. So LWG decided to simply plaster inline
all over the variable templates just to be safe, because they don't hurt, either.
[dcl.constexpr]/9
A constexpr specifier used in an object declaration declares the object as const.
[basic.link]/3.2
A name having namespace scope has internal linkage if it is the name of
-a non-inline variable of non-volatile const-qualified type that is neither explicitly declared extern nor previously declared to have external linkage
Without inline
specifier, is_integral_v
would have internal linkage. This could be problematic if you compared two pointers to this same variable name taken in different translation unit.
Nota Bene: the inline specifier is redundant with constexpr only if the variable is a class static data member.
Following an exemple of easy violation of the one definition rule that could happen if is_integral_v
where not inline.
bad_type_trait.h
template<class T>
constexpr auto bad_is_integral_v=std::is_integral<T>::value;
my_header.h
#include "bad_type_trait.h"
void f(const bool& x);
inline void g()
{
f(bad_is_integral_v<int>);
//g ODR use the static variable bad_is_integral_v.
//"ODR use" approximate definition is:
// the variable is refered by its memory address.
}
source1.cpp
#include "my_header.h"
void my_func1(){
g(); //the definition of g in this translation unit.
}
source2.cpp
#include "my_header.h"
void my_func2(){
g(); //is not the same as the definition of g in this translation unit.
}
In the two translation units, the variable bad_is_integral_v
is instantiated as separate static variables. The inline function g()
is defined in these two translation units. Inside the definition of g()
, the variable bad_is_integral_v
is ODR used, so the two definitions of g()
are different, which is a violation of the one definition rule.