89

What is the purpose of anonymous enum declarations such as:

enum { color = 1 };

Why not just declare int color = 1?

David G
  • 94,763
  • 41
  • 167
  • 253

8 Answers8

116

That's a so-called enum trick for declaring a compile-time integer constant. It's advantage is it guarantees that no variable is instantiated and therefore there's no runtime overhead. Most compilers introduce no overhead with integer constants anyway.

Community
  • 1
  • 1
sharptooth
  • 167,383
  • 100
  • 513
  • 979
  • +1: This is the correct answer. There is no valid reason for using an `enum` in place of a `const int` apart from this. – Oliver Charlesworth Aug 22 '11 at 11:59
  • 3
    +1 should be accepted answer. Great, short and sweet explanation. –  Aug 22 '11 at 12:56
  • 1
    I too like this answer. Rummaging through the kernel sources of Linux I've now become aware of this *trick*. One possible reason for using *enum* over the *const int* alternative is that using enum allows for an organized method of grouping like-purposed names. – Andrew Falanga Aug 18 '15 at 19:52
  • You need to have a valid pointer to a constant integer. While enums have no pointers at all. For this reason the compiler will still save the constant. Is up to the linker to see if the constant will be used or not. – rxantos Feb 15 '22 at 21:18
76

Enums don't take up any space and are immutable.

If you used const int color = 1; then you would solve the mutability issue but if someone took the address of color (const int* p = &color;) then space for it would have to be allocated. This may not be a big deal but unless you explicitly want people to be able to take the address of color you might as well prevent it.

Also when declaring a constant field in a class then it will have to be static const (not true for modern C++) and not all compilers support inline initialization of static const members.


Disclaimer: This answer should not be taken as advice to use enum for all numeric constants. You should do what you (or your co-workers) think is more readable. The answer just lists some reasons one might prefer to use an enum.

S.S. Anne
  • 15,171
  • 8
  • 38
  • 76
Motti
  • 110,860
  • 49
  • 189
  • 262
  • 4
    `const int` is immutable, and may not take up any space, depending on what the compiler chooses to do. – Oliver Charlesworth Aug 22 '11 at 11:52
  • @Oli Charlesworth How can constants not take up space? What if I'm initializing constant at runtime? – atoMerz Aug 22 '11 at 11:53
  • 6
    @AtoMerZ: That's why I said "it depends". Also, that's not really a fair comparison; you can't change an `enum` at runtime! – Oliver Charlesworth Aug 22 '11 at 11:54
  • 1
    I'm just saying constants don't do the same thing. constants have different purpose. – atoMerz Aug 22 '11 at 11:57
  • @AtoMerZ: if you initialize constants at runtime, then the compiler cannot optimize it out, obviously. – Matthieu M. Aug 22 '11 at 12:05
  • @AtoMerZ: In short - constants do not have to occupy space. On Cisc cpus instruction opcode may include constant within it hence there is no need to store it anywhere. On the contrary on Risc due to limited number of instructions ant their length usually only some specific consts (powers of 2 and variations) can be 'embedded' in the opcode itself. – Artur Apr 03 '12 at 17:10
  • It's the contrary: now you *have to* store it everywhere: embedded in every instruction which uses it, instead of one place in read-only data section. – SasQ Aug 12 '12 at 04:20
  • @Motti what you mean by Enums don't take up any space? in order to use the Enum, we need to declare a variable, the variable need to take up space, right? is it the same as a normal variable? –  Jan 03 '13 at 10:18
  • @ratzip you don't declare a variable you write `enum { answer = 42 };` and later use `answer` the compiler just uses the constant `42` and there's no variable in sight. – Motti Jan 03 '13 at 12:32
  • @Motti when you say "... space for it would have to be allocated", what do you mean by *it*? The pointer? – Olumide Dec 12 '14 at 14:03
  • @Olumide, the constant would have to be put in memory so that there could be a pointer to it. – Motti Dec 13 '14 at 17:21
  • I agree with the part about immutability and enforcing that the program can't use it is as a run time constant. But it isn't just a readability question for me. If you are going to assume the compiler is stupid with const, it can be stupid with enum, so eg maybe you do end up with run time space occupied, and maybe it is 64 bits per. (After all, you never specified what type to use, so the compiler just has to use something that works.) It seems likely the compiler might use the smallest size that accommodates *all* the constants. – Mike Layton Jan 18 '18 at 22:41
  • Do I understand correctly that from C++11 on, I could use `constexpr` instead of this enum trick? – hr0m Jan 09 '23 at 12:49
  • 1
    @hr0m `constexpr` solves some of the issues that the enum trick does but it potentially still causes a variable to be allocated. See https://godbolt.org/z/fG11jjssf – Motti Jan 10 '23 at 09:19
9

If this is old code, then enum might have been used for the "enum hack".

You can learn more about the "enum hack", for example, in this link: enum hack

In short: this allows defining the value once, just like #define or defining a variable, but unlike #define - here the compiler makes sure that the value is a number (int, r-value) and prevents you from doing all kinds of mischief that you can do with the precompiler's simple "search and replace", and unlike defining a variable - never takes up space under any compiler or configuration, and prevents changes to it (even const variables can sometimes be changed if you try hard enough).

Eran Zimmerman Gonen
  • 4,375
  • 1
  • 19
  • 31
  • 6
    Upvoted, but I recommend you describe the enum hack in your answer as well in case that link ever goes dead in the next 20 years. – Gabriel Staples Oct 25 '19 at 21:36
6
(1) int color = 1;

color is changeable (accidently).

(2) enum { color = 1 };

color cannot be changed.

The other option for enum is,

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

Both enum and const int offer exactly same concept; it's a matter of choice. With regards to popular belief that enums save space, IMO there is no memory constraint related to that, compiler are smart enough to optimize const int when needed.

[Note: If someone tries to use const_cast<> on a const int; it will result in undefined behavior (which is bad). However, the same is not possible for enum. So my personal favorite is enum]

iammilind
  • 68,093
  • 33
  • 169
  • 336
  • 6
    But in general, the `const int` should be preferred, because the semantics more closely match the intent (it is not an enumeration!). – Oliver Charlesworth Aug 22 '11 at 11:51
  • I think that in `const int color = 1;`, color could still be changed if you really wanted to, with `const_cast`; enum, however, can't be changed. – Eran Zimmerman Gonen Aug 22 '11 at 11:52
  • 5
    @Eran: No. If it is declared const, you can't `const_cast` the constness away: that is undefined behaviour. – R. Martinho Fernandes Aug 22 '11 at 11:54
  • 1
    @Oli, well, I will prefer `enum` over `const int`; IMO they are more organized and not likely to get victimized by `const_cast` and result in UB. – iammilind Aug 22 '11 at 11:54
  • @Eran: If you're willing to invoke undefined behaviour, then yes, a `const int` might be mutable. – Oliver Charlesworth Aug 22 '11 at 11:55
  • @Eran Zimmerman: Mutating a variable that was declared `const` yields undefined behaviour. The standard encourages to put `const`-declared things in ROM. – Sebastian Mach Aug 22 '11 at 11:55
  • 2
    @iammilind: "Much more organized"? YMMV, but my approach is to ensure my code's semantics matches my intent as closely as possible. If you're accidentally casting away constness, then you have bigger problems! – Oliver Charlesworth Aug 22 '11 at 11:56
  • To be specific, @Sebastian that would mean they are likely stored in `.rodata`, yes? (I guess `.text` is possible; that is read-only, I believe, but is this ever actually done? Maybe when a `.rodata` doesn't exist and they'd rather use `.text` than `.data` so it's read-only?) – RastaJedi Aug 27 '16 at 23:46
6

One use of this is when you're doing template metaprogramming, because enum objects are not lvalues, while static const members are. It also used to be a common workaround for compilers that didn't let you initialize static integral constants in the class definition. This is explained in another question.

Community
  • 1
  • 1
R. Martinho Fernandes
  • 228,013
  • 71
  • 433
  • 510
2

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

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

// (…)
2

When you use
enum {color = 1}
you're not using any memory it's like
#define color 1

If you declare a variable
int color=1 Then you're taking up memory for a value that's not to be changed.

atoMerz
  • 7,534
  • 16
  • 61
  • 101
  • 2
    You're not taking up any memory unless the compiler decides to allocate memory for it. – Oliver Charlesworth Aug 22 '11 at 11:58
  • By "definition" constants are variables and take up space, I'm not talking about compiler specific optimizations. – atoMerz Aug 22 '11 at 12:00
  • 2
    "taking up space" is irrelevant unless you're talking about the compiler output. And if you're worried about taking up space, then you are worrying about optimizations. No sane compiler would allocate space for a variable that is not written to. – Oliver Charlesworth Aug 22 '11 at 12:01
  • `const int x=1; const int* y=&x; cout << y;`. Nothing is written into x after declaration. You might wanna see the result of this. – atoMerz Aug 22 '11 at 12:19
  • 2
    Ok, you've found another case where the compiler can't avoid allocating memory. But that doesn't detract from my main point. – Oliver Charlesworth Aug 22 '11 at 12:36
  • Can you actually redirect me to a resource that says (any) compilers do so? Is there a way I can find out on my own? – atoMerz Aug 23 '11 at 10:28
  • See this question that was asked yesterday: http://stackoverflow.com/questions/7148023/const-int-does-not-take-up-space. – Oliver Charlesworth Aug 23 '11 at 10:57
  • This answer is fixed by adding one word: "asking." When you declare a variable, you are asking the compiler allocate space. It is only later that the compile *may* decide to ignore your request. Hopefully any *sane* compiler has an option to turn off this optimization so you can measure the size of your variable space even if your code isn't completely written. Whether you put const in front complicates things, but doesn't negate this answer. – Mike Layton Jan 18 '18 at 22:50
0

I dont see it mentioned, another use is to scope your constants. I currently work on code that was written using Visual Studio 2005, and it is now ported to android - g++. In VS2005 you could have code like this enum MyOpts { OPT1 = 1 }; and use it as MyOpts::OPT1 - and compiler did not complain about it, even though it is not valid. g++ reports such code as error, so one solution is to use anonymous enum as follows: struct MyOpts { enum {OPT1 =1}; };, and now both compilers are happy.

marcinj
  • 48,511
  • 9
  • 79
  • 100