Почему cppreference определяет тип_копирования xxx_v ярлыков как inline constexpr, а не только constexpr?
Почему cppreference определяет ярлыки xxx_v типа_traits как inline constexpr
, а не только constexpr
?
Например, см. is_integral_v
:
template< class T >
inline constexpr bool is_integral_v = is_integral<T>::value;
Это только вопрос стиля или есть какая-то разница в поведении? Насколько я знаю, переменные constexpr
неявно inline
.
Изменить: смотря на черновик нового стандарта, он также использует inline constexpr
. Тогда вопрос действительно относится к стандарту.
Ответы
Ответ 1
[basic.link]/3.2 применяется к именам "не встроенной переменной нестабильного типа с константой". Шаблон переменной не является переменной, так же как шаблон класса не является классом, шаблон функции не является функцией, а куттер-резак не является файлом cookie. Таким образом, это правило не применяется к самому шаблону is_integral_v
.
Специализация переменной шаблона - это переменная, однако есть некоторые вопросы о том, дает ли это правило внутреннюю связь. Это основная проблема 1713, направление которой в том, что правило не применимо.
Основная проблема 1713, однако, не была своевременно разрешена для С++ 17. Поэтому LWG решила просто штукатурку inline
по всем шаблонам переменных просто быть в безопасности, потому что они тоже не болят.
Ответ 2
[dcl.constexpr]/9
Спецификатор constexpr, используемый в объявлении объекта, объявляет объект как const.
[basic.link]/3.2
Имя, имеющее область пространства имен, имеет внутреннюю связь, если это имя
-a non-inline переменная из энергонезависимого const-квалифицированного типа, которая не является явно объявленной extern или ранее объявленной как внешняя связь
Без спецификатора inline
, is_integral_v
будет иметь внутреннюю связь. Это может быть проблематично, если вы сравнили два указателя на это же имя переменной, взятое в разных единицах перевода.
Nota Bene: спецификатор inline избыточен с constexpr, только если переменная является элементом статических данных класса.
Следуя примеру легкого нарушения одного правила определения, которое может произойти, если is_integral_v
где не встроено.
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.
}
В двух единицах перевода переменная bad_is_integral_v
создается как отдельные статические переменные. Встроенная функция g()
определена в этих двух единицах перевода. Внутри определения g()
переменная bad_is_integral_v
используется ODR, поэтому два определения g()
различны, что является нарушением одного правила определения.