Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using enum instead of struct for tag dispatching in C++

Let's take implementation of the std::unique_lock from the Standard Library:

struct defer_lock_t { explicit defer_lock_t() = default; };
struct try_to_lock_t { explicit try_to_lock_t() = default; };
struct adopt_lock_t { explicit adopt_lock_t() = default; };

inline constexpr defer_lock_t  defer_lock {};
inline constexpr try_to_lock_t try_to_lock {};
inline constexpr adopt_lock_t  adopt_lock {};

unique_lock (mutex_type& m, defer_lock_t t) noexcept;
unique_lock (mutex_type& m, try_to_lock_t t);
unique_lock (mutex_type& m, adopt_lock_t t);

Is there a reason why one wouldn't/couldn't/shouldn't use enums instead of structs to implement tag dispatching? Such as:

enum defer_lock_t { defer_lock };
enum try_to_lock_t { try_to_lock };
enum adopt_lock_t { adopt_lock };

unique_lock (mutex_type& m, defer_lock_t t) noexcept;
unique_lock (mutex_type& m, try_to_lock_t t);
unique_lock (mutex_type& m, adopt_lock_t t);

The latter is more concise.

The only advantage of using structs that I can think of is inheritance (eg, iterator tags). But in all other cases, why not use enum?

like image 489
Innocent Bystander Avatar asked Aug 16 '18 16:08

Innocent Bystander


People also ask

When would you use a struct vs an enum?

One of the vital difference between structs and enums is that an enum doesn't exist at run-time. It's only for your benefit when you're read/writing the code. However, instances of structs (and classes) certainly can exist in memory at runtime.

Is enum same as struct?

struct - Used to define a structure type. enum - Used to define an enumeration.

Can we use enum in struct?

@itmanwork You can declare an enum instance inside a struct, but you cannot use typedef.

Do enums wrap around?

No. enum s are not designed to "wrap around" in the way you describe by default.


1 Answers

Another (minor) benefit on top of the reasons listed by Barry is that if the function call is not inlined, the enum tags have state that needs to be passed into the function, whereas struct tags do not. Even if it looks like the enum is empty, unscoped enumerations always have at least one byte of state that can be cast into them, and scoped enumerations always have at least one bit of state. See http://eel.is/c++draft/enum#dcl.enum-7

Given

struct s {};
enum e {};

void a(s);
void b(e);

void c() {
    a(s());
}

void d() {
    b(e());
}

clang and gcc for 64-bit Linux both generate

c():                                  # @c()
        jmp     a(s)                          # TAILCALL
d():                                  # @d()
        xor     edi, edi
        jmp     b(e)                          # TAILCALL

But note that on Windows, the calling convention seems to prevent this (MSVC code gen):

$T1 = 8
void c(void) PROC                                      ; c, COMDAT
        movzx   ecx, BYTE PTR $T1[rsp]
        jmp     void a(s)                   ; a
void c(void) ENDP                                      ; c

void d(void) PROC                                      ; d, COMDAT
        xor     ecx, ecx
        jmp     void b(e)                            ; b
void d(void) ENDP                                      ; d

See it live: https://godbolt.org/z/ss7Ke64ca

like image 127
David Stone Avatar answered Sep 19 '22 15:09

David Stone