让我们从标准库中实现
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);
是否有理由不/不能/不应该使用枚举而不是结构来实现标签调度?如:
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);
后者更简洁。
我能想到的使用结构的唯一优点是继承(例如,迭代器标签)。但在所有其他情况下,为什么不使用枚举呢?
首先,您不希望标签类型是可构造的,您想明确地命名它们。这并不特别适用于
{}
,因为 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));
的实施是一个胜利。标准库中的标签类型并不多。
除了 Barry 列出的原因之外,另一个(次要)好处是,如果函数调用不是内联的,则枚举标记具有需要传递到函数中的状态,而结构标记则不需要。即使看起来枚举是空的,无作用域枚举始终具有至少一个可以转换到其中的状态字节,而作用域枚举始终具有至少一位状态。请参阅 http://eel.is/c++draft/enum#dcl.enum-7