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

Использование анонимных перечислений

Какова цель анонимных объявлений enum, таких как:

enum { color = 1 };

Почему бы просто не объявить int color = 1?

4b9b3361

Ответ 1

Перечисления не занимают места и неизменяемы.

Если вы использовали const int color = 1;, тогда вы решили бы проблему с изменчивостью, но если бы кто-то взял адрес color (const int* p = &color;), тогда для этого нужно было бы выделить пространство для него. Это не может быть большой проблемой, но если вы явно не хотите, чтобы люди могли принимать адрес color, вы могли бы также предотвратить его.

Также при объявлении константного поля в классе он должен быть static const (недействителен для современного С++) и не все компиляторы поддерживают встроенную инициализацию статических константных членов.


Отказ от ответственности: Этот ответ не следует принимать за рекомендации по использованию enum для всех числовых констант. Вы должны делать то, что вы (или ваши корова-орки) считаете более читаемыми. Ответ просто перечисляет некоторые причины, по которым можно было бы использовать enum.

Ответ 2

Это так называемый enum trick для объявляющий константу целочисленного времени компиляции. Преимущество заключается в том, что он гарантирует, что никакая переменная не создается и, следовательно, нет накладных расходов во время выполнения. Большинство компиляторов вообще не вносят никаких накладных расходов с целыми константами.

Ответ 3

Если это старый код, то для "enum hack" может быть использовано перечисление.

Вы можете узнать больше о "enum hack", например, по этой ссылке: enum hack

Ответ 4

Одно использование этого - это когда вы делаете метапрограммирование шаблона, потому что объекты перечисления не lvalues, а static const - члены. Это также было распространенным обходным решением для компиляторов, которое не позволяло вам инициализировать статические интегральные константы в определении класса. Это объясняется в другом вопросе.

Ответ 5

(1) int color = 1;

color является изменяемым (случайно).

(2) enum { color = 1 };

color не может быть изменен.

Другим вариантом для enum является

const int color = 1;  // 'color' is unmutable

Оба enum и const int предлагают точно такую ​​же концепцию; это вопрос выбора. Что касается распространенного мнения, что enum сэкономить место, IMO не связано с этим ограничением памяти, компилятор достаточно умен, чтобы оптимизировать const int при необходимости.

[Примечание: если кто-то пытается использовать const_cast<> на const int; это приведет к поведению undefined (что плохо). Однако для enum это невозможно. Поэтому мой личный фаворит enum]

Ответ 6

Когда вы используете  enum {color = 1}
 вы не используете память, как будто  #define color 1

Если вы объявите переменную  int color=1  Затем вы берете память за значение, которое нельзя изменить.

Ответ 7

Я не вижу в нем упоминания, другое использование - охват ваших констант. В настоящее время я работаю над кодом, написанным с использованием Visual Studio 2005, и теперь он портирован на android-g++. В VS2005 у вас может быть такой код enum MyOpts { OPT1 = 1 }; и использовать его как MyOpts:: OPT1, и компилятор не жаловался на него, хотя он и недействителен. g++ сообщает такой код как ошибку, поэтому одним из решений является использование анонимного enum следующим образом: struct MyOpts { enum {OPT1 =1}; };, и теперь оба компилятора счастливы.

Ответ 8

Ответ

Читаемость и производительность.
Details are describbed as notes to examples below.

Варианты использования

Личный пример

В Unreal Engine 4 (игровой движок C++) у меня есть следующее свойство (переменная-член, доступная для движка):

/// Floor Slope.

UPROPERTY
(
    Category = "Movement",
    VisibleInstanceOnly,

    BlueprintGetter = "BP_GetFloorSlope",
    BlueprintReadOnly,

    meta =
    (
        ConsoleVariable = "Movement.FloorSlope",
        DisplayName     = "Floor Slope",
        ExposeOnSpawn   = true,
        NoAutoLoad
    )
)

float FloorSlope = -1.f;

Это значение наклона, на котором игрок стоит (значение ∈ [0; 90) °), если таковое имеется.
Из-за ограничений двигателя он не может быть ни std::optional, ни TOptional.
Я нашел решение добавить еще одну самоопределяемую переменную bIsOnFloor.

bool  bIsOnFloor = false;

Мой внутренний установщик C++ только для FloorSlope имеет следующий вид:

void UMovement::SetFloorSlope(const float& FloorSlope) noexcept
    contract [[expects audit: FloorSlope >= 0._deg && FloorSlope < 90._deg]]
{
    this->bIsOnFloor = true;
    this->FloorSlope = FloorSlope;

    AUI::UI->Debug->FloorSlope = FString::Printf(L"Floor Slope: %2.0f", FloorSlope);
};

Добавление особого случая, когда параметр FloorSlope будет принимать аргумент -1.f, будет трудно угадать и не будет удобным для пользователя. Вместо этого я бы лучше создал поле False enum:

enum { False };

Таким образом, я могу просто перегрузить функцию SetFloorSlope, которая использует False вместо -1.f.

void UMovement::SetFloorSlope([[maybe_unused]] const decltype(False)&) noexcept
{
    this->bIsOnFloor = false;
    this->FloorSlope = -1.f;

    AUI::UI->Debug->FloorSlope = L"Floor Slope:  —";
};


Когда персонаж игрока попадает на пол после применения силы тяжести к нему в тик, я просто вызываю:

SetFloorSlope(FloorSlope);

… где FloorSlope - это значение float ∈ [0; 90) °. В противном случае (если он не попадает в пол), я звоню:

SetFloorSlope(False);

Эта форма (в отличие от прохождения -1.f) гораздо более читабельна и не требует пояснений.

Пример двигателя

Другим примером может быть предотвращение или принудительная инициализация. Упомянутый выше Unreal Engine 4 обычно использует FHitResult struct, содержащую информацию об одном попадании следа, например, о точке удара и нормали поверхности в этой точке.

Этот сложный struct вызывает метод Init по умолчанию, устанавливая некоторые значения для определенных переменных-членов. Это может быть принудительно или запрещено (общедоступные документы: FHitResult #constructor):

FHitResult()
{
    Init();
}

explicit FHitResult(float InTime)
{
    Init();
    Time = InTime;
}

explicit FHitResult(EForceInit InInit)
{
    Init();
}

explicit FHitResult(ENoInit NoInit)
{
}

Epic Games определяет подобное enum, но добавляет избыточные имена enum:

enum EForceInit 
{
    ForceInit,
    ForceInitToZero
};
enum ENoInit {NoInit};

Передача NoInit в конструктор FHitResult предотвращает инициализацию, что может привести к увеличению производительности, если не инициализировать значения, которые будут инициализированы в другом месте.

Пример сообщества

FHitResult(NoInit) Использование в DamirH посте в серии анализов всестороннего игрового процесса:

//A struct for temporary holding of actors (and transforms) of actors that we hit
//that don't have an ASC. Used for environment impact GameplayCues.
struct FNonAbilityTarget
{
    FGameplayTagContainer CueContainer;
    TWeakObjectPtr<AActor> TargetActor;
    FHitResult TargetHitResult;
    bool bHasHitResult;

public:
    FNonAbilityTarget()
        : CueContainer(FGameplayTagContainer())
        , TargetActor(nullptr)
        , TargetHitResult(FHitResult(ENoInit::NoInit))
        , bHasHitResult(false)
    {
    }

// (…)