在C++中使用enum而不是struct进行标签分派

Inn*_*der 10 c++ c++17 tag-dispatching

让我们std::unique_lock从标准库中实现:

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);
Run Code Online (Sandbox Code Playgroud)

是否有人不会/不能/不应该使用枚举而不是结构来实现标签调度?如:

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);
Run Code Online (Sandbox Code Playgroud)

后者更简洁.

使用我可以想到的结构的唯一优点是继承(例如,迭代器标记).但在所有其他情况下,为什么不使用枚举?

Bar*_*rry 11

首先,您不希望标记类型是可{}构造的,您希望明确命名它们.这并不具体适用于unique_lock因为unique_lock<std::mutex> lk(m, {})含糊不清,但有一般原则.标记类型的设计使您必须编写std::defer_lock(或者,如果您真的需要 std::defer_lock_t()).

其次,您实际上只想在要使用它们的特定上下文中使用标记类型.如果你制作它们enum,那么你引入了所有的enum功能 - 比如可以转换成整数:

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));
Run Code Online (Sandbox Code Playgroud)

这些其他表达没有任何意义,所以即使它们存在也不错.

第三,在只是一个小的,固定数量的字符的情况下实现标准库的简洁并不是一个大问题.所以我甚至不认为enum实施是一场胜利.标准库中没有那么多标记类型.