Подтвердить что ты не робот

В чертах типа, почему люди используют enum, а не static const для значения?

Например, так я бы написал его, и он компилируется и работает отлично:

template<typename T> struct is_pointer<T*> {
  static const bool value = true;
}

Тогда почему некоторые люди пишут менее очевидные

template<typename T> struct is_pointer<T*> {
  enum { value = true };
}      

вместо этого? Это связано только с тем, что переменная static const использует байты памяти, тогда как enum не?

4b9b3361

Ответ 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] делают все остальное, поскольку они правят ссылкой и временными ссылками.

Ответ 2

Это только потому, что переменная static const использует байты памяти, тогда как enum не?

Да, это причина.

static const bool value = true;

будет занимать память, а

enum { value = true };

нет.

Ответ 3

Да, вы правы: enum { value = true }; не занимает никакой памяти.

Кроме того, до С++ 11 это был почти единственный способ достижения этого: static const bool value = true; является только законным в определении класса из С++ 11 и далее. Хотя предпочтительнее использовать constexpr.

Ответ 4

Он также является другим символом в каждом объектном файле, который включает его, без какой-либо выгоды. Если вы используете сгибание символов (-gc-sections), у вас не будет отделенных разделов и раздуваются ваши двоичные файлы.