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?
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.
struct - Used to define a structure type. enum - Used to define an enumeration.
@itmanwork You can declare an enum instance inside a struct, but you cannot use typedef.
No. enum s are not designed to "wrap around" in the way you describe by default.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With