The usage of anonymous enums

后端 未结 8 728
无人及你
无人及你 2020-11-29 19:40

What is the purpose of anonymous enum declarations such as:

enum { color = 1 };

Why not just declare int color = 1

8条回答
  •  臣服心动
    2020-11-29 20:39

    Answer

    Readability and performance.
    Details are describbed as notes to examples below.

    Use cases

    Personal example

    In Unreal Engine 4 (C++ game engine), I have following property (engine exposed member variable):

    /// Floor Slope.
    
    UPROPERTY
    (
        Category = "Movement",
        VisibleInstanceOnly,
    
        BlueprintGetter = "BP_GetFloorSlope",
        BlueprintReadOnly,
    
        meta =
        (
            ConsoleVariable = "Movement.FloorSlope",
            DisplayName     = "Floor Slope",
            ExposeOnSpawn   = true,
            NoAutoLoad
        )
    )
    
    float FloorSlope = -1.f;
    

    This is a value of floor slope player is standing on (value ∈ [0; 90)°), if any.
    Because of engine limitations, it cannot be neither std::optional nor TOptional.
    I've came up with a solution to add another self explainable variable bIsOnFloor.

    bool  bIsOnFloor = false;
    

    My C++ only internal setter for FloorSlope takes the following form:

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

    Adding special case where FloorSlope parameter would take argument of -1.f would be hard to guess and not user friendly. Instead, I'd rather create False enum field:

    enum { False };
    

    This way, I can simply overload SetFloorSlope function that takes intuitive False instead of -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:  —";
    };
    


    When a player character hits a floor upon applying gravity to it on tick, I simply call:

    SetFloorSlope(FloorSlope);
    

    … where FloorSlope is a float value ∈ [0; 90)°. Otherwise (if it does not hits a floor), I call:

    SetFloorSlope(False);
    

    This form (as opposed to passing -1.f) is much more readable, and self explanatory.

    Engine example

    Another example may be to prevent or force initialization. Mentioned above Unreal Engine 4 commonly uses FHitResult struct containing information about one hit of a trace, such as point of impact and surface normal at that point.

    This complex struct calls Init method by default, setting some values to certain member variables. This can be forced or prevented (public docs: FHitResult #constructor):

    FHitResult()
    {
        Init();
    }
    
    explicit FHitResult(float InTime)
    {
        Init();
        Time = InTime;
    }
    
    explicit FHitResult(EForceInit InInit)
    {
        Init();
    }
    
    explicit FHitResult(ENoInit NoInit)
    {
    }
    

    Epic Games defines such enums similiar, but adds redundant enum names:

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

    Passing NoInit to the constructor of FHitResult prevents initialization, what can lead to performance gain by not initializing values that will be initialized elsewhere.

    Community example

    FHitResult(NoInit) usage in DamirH's post on Comprehensive GameplayAbilities Analysis Series:

    //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 TargetActor;
        FHitResult TargetHitResult;
        bool bHasHitResult;
    
    public:
        FNonAbilityTarget()
            : CueContainer(FGameplayTagContainer())
            , TargetActor(nullptr)
            , TargetHitResult(FHitResult(ENoInit::NoInit))
            , bHasHitResult(false)
        {
        }
    
    // (…)
    

提交回复
热议问题