IT TIP

익명 열거 형 사용

itqueen 2020. 11. 19. 22:47
반응형

익명 열거 형 사용


다음 enum과 같은 익명 선언 의 목적은 무엇입니까?

enum { color = 1 };

왜 선언하지 int color = 1않습니까?


열거 형은 공간을 차지하지 않으며 변경할 수 없습니다.

사용했다면 const int color = 1;가변성 문제를 해결할 수 있지만 누군가 color( const int* p = &color;) 주소를 가져간 경우 공간을 할당해야합니다. 이것은 큰 문제는 아니지만 사람들이 귀하 의 주소를 가져갈 수 있도록 명시 적으로 원하지 않는 한이를 color방지 할 수 있습니다.

또한 클래스에서 상수 필드를 선언 할 때 static const (최신 C ++에서는 해당 되지 않음) 모든 컴파일러가 정적 const 멤버의 인라인 초기화를 지원하는 것은 아닙니다.


면책 조항 : 이 답변은 enum모든 숫자 상수에 대한 조언으로 간주되어서는 안됩니다 . 당신 (또는 당신의 소꾼)이 더 읽기 쉽다고 생각하는 것을해야합니다. 대답은 하나는 몇 가지 이유가 나열 할 수 를 사용하는 것을 선호를 enum.


이것은 컴파일 시간 정수 상수선언하는 소위 열거 형 트릭입니다 . 장점은 변수가 인스턴스화되지 않으므로 런타임 오버 헤드가 없다는 것입니다. 어쨌든 대부분의 컴파일러는 정수 상수로 오버 헤드를 발생시키지 않습니다.


이것이 오래된 코드라면 enum이 "enum hack"에 사용되었을 수 있습니다.

예를 들어 다음 링크에서 "enum hack"에 대해 자세히 알아볼 수 있습니다. enum hack


이것의 한 가지 용도는 템플릿 메타 프로그래밍을 할 때입니다. enum 객체는 lvalue가 아니지만 static const멤버는 있기 때문 입니다. 또한 클래스 정의에서 정적 정수 상수를 초기화 할 수없는 컴파일러의 일반적인 해결 방법이기도했습니다. 이것은 다른 질문에 설명되어 있습니다.


(1) int color = 1;

color 변경 가능합니다 (실수로).

(2) enum { color = 1 };

color 변경할 수 없습니다.

다른 옵션 enum

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

모두 enumconst int정확히 동일한 개념을 제공; 그것은 선택의 문제입니다. enum공간을 절약 한다는 대중적인 믿음과 관련하여 IMO에는 이와 관련된 메모리 제약이 없으며 컴파일러는 const int필요할 때 최적화 할 수 있을만큼 똑똑 합니다.

[참고 : 누군가가에 사용하려고 const_cast<>하면 const int; 정의되지 않은 동작이 발생합니다 (나쁜). 그러나 enum. 그래서 개인적으로 가장 좋아하는 것은 enum]


사용할 때
enum {color = 1}
메모리를 사용하지 않는 것은
#define color 1

변수를 선언하면
int color=1변경되지 않는 값을 위해 메모리를 차지합니다.


나는 그것을 언급하지 않았고, 또 다른 용도는 상수 범위를 지정하는 것입니다. 저는 현재 Visual Studio 2005를 사용하여 작성된 코드를 작업하고 있으며 이제 android-g ++로 포팅되었습니다. VS2005에서는 이와 같은 코드 enum MyOpts { OPT1 = 1 };를 사용하여 MyOpts :: OPT1로 사용할 수 있으며 컴파일러는 유효하지 않더라도 이에 대해 불평하지 않았습니다. g ++는 이러한 코드를 오류로보고하므로 한 가지 해결책은 다음과 같이 익명 열거 형을 사용 struct MyOpts { enum {OPT1 =1}; };하는 것입니다. 이제 두 컴파일러 모두 만족합니다.


대답

가독성 및 성능.
자세한 내용은 아래 예제에 대한 참고 사항으로 설명되어 있습니다.

사용 사례

개인적인 예

에서 언리얼 엔진 4 (C ++ 게임 엔진), I 속성 (엔진 노출 멤버 변수를) 다음있다 :

/// 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 ++ 전용 내부 setter 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.NET 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);

… 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<AActor> TargetActor;
    FHitResult TargetHitResult;
    bool bHasHitResult;

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

// (…)

참고URL : https://stackoverflow.com/questions/7147008/the-usage-of-anonymous-enums

반응형