Enum vs неизменяемый в D

Какая разница между

enum i = 2;
enum s = "Hello";

и

immutable i = 2;
immutable s = "Hello";

в D 2.0?

Ответы

Ответ 1

An enum - это определяемый пользователем тип, а не переменная. enum e = 2; - это короткую руку для чего-то вроде этого enum : int { e = 2 } (т.е. анонимного перечисление с одним членом e), см. документацию. По определению все члены анонимного перечисления помещаются в текущий объем. Таким образом, e является членом типа, помещенным в текущую область действия, где он ведет себя как literal. immutable i = 2;, с другой стороны, фактически создает переменную i типа int.

Это различие имеет несколько следствий:

  • enum e не будет иметь место для памяти и нет адреса (не является lvalue), поскольку ни тип, ни его члены не имеют адреса. То есть вы ничего не можете сделать например auto ptr = &e; (так же, как вы не можете сделать auto ptr = &2;). immutable i С другой стороны, это нормальная переменная (просто неизменная).
  • Как обсуждалось Джонатаном, неизменяемые переменные могут быть инициализированы во время компиляции или во время выполнения, тогда как тип (со всеми его членами, определяющими тип) должен быть известен в время компиляции.
  • Компилятор может просто заменить все элементы e на 2. Для i это обычно необходимо создать ячейку памяти (хотя оптимизирующий компилятор возможно, сможет избежать этого иногда). По этой причине рабочая нагрузка во время можно ожидать, что компиляция для enum будет несколько ниже, и несколько меньше.
  • Существует удивительная разница для массивов. Для enum uint[2] E = [0, 1]; и immutable uint[2] I = [0, 1]; доступ к enum, например. E[0], может на порядок медленнее, чем для массива immutable, например. I[0] особенно когда массивы e и i становятся больше. Это происходит потому, что для immutable, это обычный поиск массива, скажем, глобального переменная. Для enum однако похоже, что массив создается каждый времени до его использования, например. внутри функции для глобального enum (не спросите меня, почему, но компилятор действительно просто заменяет внешний вид со значением в этом случае тоже). Я никогда не пробовал, но догадывался, что то же самое относится к строкам enum и другим нетривиальным типам.

Подводя итог: когда я использую константы времени компиляции, я обычно беру enum, если только эти константы являются массивами или мне нужна ячейка памяти по какой-то другой причине.

Ответ 2

перечисления всегда инициализируются во время компиляции. Таким образом, им должны быть присвоены значения, которые могут быть созданы с помощью CTFE (Compile Time Function Evaluation).

неизменяемые переменные могут быть инициализированы во время выполнения. Если неизменяемая переменная имеет глобальное время жизни (так что это переменные модуля или статический класс или статическая локальная переменная), то она должна быть либо инициализирована во время компиляции, либо во время выполнения со статическим конструктором (хотя статические локальные переменные не могут назначается статическим конструктором). Если неизменяемая переменная является нестатической локальной переменной, то она инициализируется во время выполнения (хотя, если значение является константой, тогда компилятор может ее оптимизировать и инициализировать во время компиляции). Таким образом, вы можете создавать неизменяемые локальные переменные во время выполнения, в отличие от перечислений.

EDIT: Еще один случай, который я забыл: неизменные переменные-члены должны либо инициализироваться непосредственно CTFE, либо инициализироваться с помощью неизменяемого конструктора. Если переменная неизменяемого члена инициализируется непосредственно CTFE, то, очевидно, это делается во время компиляции, тогда как инициализация ее в неизменяемом конструкторе выполняется во время выполнения.