Ответ 1
Значительная разница заключается в том, что следующий код компилируется и содержит ссылки:
template<typename>
struct is_pointer { };
template<typename T>
struct is_pointer<T*> {
enum { value = true };
};
void f(const bool &b) { }
int main() {
f(is_pointer<void*>::value);
}
Вместо этого не работает (вы получаете ссылку undefined на value
):
template<typename>
struct is_pointer { };
template<typename T>
struct is_pointer<T*> {
static const bool value = true;
};
void f(const bool &b) { }
int main() {
f(is_pointer<void*>::value);
}
Конечно, это не работает, если вы не добавили где-то следующие строки:
template<typename T>
const bool is_pointer<T*>::value;
Это из-за [class.static.data]/3 (выделено мной):
Если постоянный элемент статистики, не входящий в состав константы, является интегральным или перечисляемым, его объявление в определении класса может указывать логический или равный-инициализатор, в котором каждое предложение-инициализатор, являющееся выражением присваивания является константным выражением ([expr.const]). Член все еще должен быть определен в области пространства имен, если в программе используется ([basic.def.odr]) в программе, а определение области пространства имен не должно содержать инициализатор. [...]
Другими словами, static const bool value = true;
- это объявление, а не определение, и вы не можете использовать odr-use value
.
С другой стороны, согласно [dcl.enum/1] (выделено мной):
Перечисление представляет собой отдельный тип с именованными константами.
Именованные константы могут ссылаться на const, как показано в примере выше.
В качестве побочного примечания что-то подобное применяется, если вы используете члены данных static
constexpr
в С++ 11/14:
template<typename T>
struct is_pointer<T*> { static constexpr bool value = true; };
Это не работает, и как я обнаружил тонкие различия между ними.
Я нашел помощь здесь, на SO, получив некоторые приятные намеки на ответ, который мне дал.
Ссылки на стандарт - это плюс, чтобы лучше объяснить, что происходит под капотом.
Обратите внимание, что объявление элемента данных static
constexpr
, подобное приведенному выше, также является определением с С++ 17. Поэтому вам больше не нужно будет это определять, и вы сможете использовать odr-use прямо.
Как уже упоминалось в комментариях (спасибо @Yakk, который подтвердил это), я также пытаюсь объяснить, как происходит, что вышеупомянутые константы-константы привязываются к константной ссылке.
[expr.const/3] вводит интегральное постоянное выражение и упоминает unscoped enum
, говоря, что он неявно преобразован в prvalue.
[dcl.init.ref/5] и [class.temporary/2] делают все остальное, поскольку они правят ссылкой и временными ссылками.