Search code examples
c++c++17language-designunique-locktag-dispatching

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?


Solution

  • First, you don't want the tag types to be {}-constructible, you want to explicitly name them. This doesn't specifically apply to unique_lock since unique_lock<std::mutex> lk(m, {}) would be ambiguous, but there's a general principle. Tag types are designed such that you have to write std::defer_lock (or, if you really want, std::defer_lock_t()).

    Second, you really only want to use the tag types in the specific context in which they're intended to be used. If you make them enums, then you bring in all the enum functionality - like being convertible to integers:

    std::make_unique<int>(std::defer_lock); // ok?
    
    // is this like super deferred?
    auto x = std::defer_lock * 2;
    
    // what do you get when you multiply six by nine?
    std::unique_lock lk(m, static_cast<std::defer_lock_t>(42));
    

    These other expressions make no sense, so it'd be nice to not even have them exist.

    Third, the concision of implementing the standard library in the case where it's just a small, fixed number of characters isn't really a big concern. So I wouldn't even see the enum implementation as a win. There aren't that many tag types in the standard library.