87 votes

L'utilisation des enums anonymes

Quel est le but de l'anonymat enum des déclarations telles que :

enum { color = 1 };

Pourquoi ne pas simplement déclarer int color = 1 ?

2voto

atoMerz Points 2069

Lorsque vous utilisez
enum {color = 1}
tu n'utilises pas de mémoire, c'est comme si
#define color 1

Si vous déclarez une variable
int color=1 Alors vous prenez de la mémoire pour une valeur qui ne doit pas être modifiée.

2voto

Réponse :

Lisibilité et performance.
<sup>Les détails sont décrits dans les notes aux exemples ci-dessous.</sup>

Cas d'utilisation

Exemple personnel

Sur Unreal Engine 4 (moteur de jeu C++), j'ai la propriété suivante (variable membre exposée du moteur) :

/// Floor Slope.

UPROPERTY
(
    Category = "Movement",
    VisibleInstanceOnly,

    BlueprintGetter = "BP_GetFloorSlope",
    BlueprintReadOnly,

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

float FloorSlope = -1.f;

Il s'agit d'une valeur de la pente du sol sur laquelle le joueur se trouve (valeur ∈ [0 ; 90)°), le cas échéant.
En raison des limitations du moteur, il ne peut être ni std::optional ni TOptional .
J'ai trouvé une solution pour ajouter une autre variable auto-explicative. bIsOnFloor .

bool  bIsOnFloor = false;

Mon seul setter interne C++ pour FloorSlope prend la forme suivante :

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);
};

Ajoutant le cas particulier où FloorSlope prendrait l'argument de -1.f serait difficile à deviner et peu convivial. À la place, je préfère créer False enum champ :

enum { False };

De cette façon, je peux simplement surcharger SetFloorSlope fonction qui prend intuitivement False au lieu de -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:  —";
};

Quand un personnage joueur frappe un sol en appliquant la gravité sur celui-ci, j'appelle simplement :

SetFloorSlope(FloorSlope);

FloorSlope est un float valeur ∈ [0 ; 90)°. Sinon (s'il ne frappe pas un plancher), j'appelle :

SetFloorSlope(False);

Cette forme (par opposition au passage -1.f ) est beaucoup plus lisible et explicite.

Exemple de moteur

Un autre exemple peut être d'empêcher ou de forcer l'initialisation. Mentionné ci-dessus, Unreal Engine 4 utilise couramment FHitResult struct contenant des informations sur un impact d'une trace, comme le point d'impact et la normale de la surface à ce point.

Ce complexe struct appelle Init par défaut, en fixant certaines valeurs à certaines variables membres. Ceci peut être forcé ou empêché (docs publics : FHitResult #constructeur ) :

FHitResult()
{
    Init();
}

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

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

explicit FHitResult(ENoInit NoInit)
{
}

Epic Games définit ainsi enum est similaire, mais ajoute une redondance enum des noms :

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

Passing NoInit au constructeur de FHitResult empêche l'initialisation, ce qui peut conduire à un gain de performance en n'initialisant pas des valeurs qui seront initialisées ailleurs.

Exemple de communauté

FHitResult(NoInit) l'utilisation dans l'article de DamirH poste sur Série d'analyses complètes du gameplay et des capacités :

//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)
    {
    }

// (…)

0voto

marcin_j Points 12237

Je ne l'ai pas vu mentionné, mais une autre utilisation est d'étendre vos constantes. Je travaille actuellement sur du code qui a été écrit avec Visual Studio 2005, et qui est maintenant porté sur Android - g++. Dans VS2005, vous pouviez avoir un code comme celui-ci enum MyOpts { OPT1 = 1 }; et l'utiliser comme MyOpts::OPT1 - et le compilateur ne s'en est pas plaint, même s'il n'est pas valide. g++ rapporte un tel code comme une erreur, donc une solution est d'utiliser un enum anonyme comme suit : struct MyOpts { enum {OPT1 =1}; }; et maintenant les deux compilateurs sont heureux.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X