C++ 为什么std::unique_lock使用类型标记来区分构造函数?

C++ 为什么std::unique_lock使用类型标记来区分构造函数?,c++,c++11,C++,C++11,在C++11中,重载以接受类型标记延迟锁定,尝试锁定,以及采用锁定: unique_lock( mutex_type& m, std::defer_lock_t t ); unique_lock( mutex_type& m, std::try_to_lock_t t ); unique_lock( mutex_type& m, std::adopt_lock_t t ); 这些是空类(类型标记): 这允许用户通过传递以下类之一来消除三个构造函数之间的歧义: const

在C++11中,重载以接受类型标记
延迟锁定
尝试锁定
,以及
采用锁定

unique_lock( mutex_type& m, std::defer_lock_t t );
unique_lock( mutex_type& m, std::try_to_lock_t t );
unique_lock( mutex_type& m, std::adopt_lock_t t );
这些是空类(类型标记):

这允许用户通过传递以下类之一来消除三个构造函数之间的歧义:

constexpr std::defer_lock_t defer_lock {};
constexpr std::try_to_lock_t try_to_lock {};
constexpr std::adopt_lock_t adopt_lock {};
我感到惊讶的是,它没有实现为
enum
。据我所知,使用
枚举将:

  • 更容易实现
  • 不要改变语法
  • 允许在运行时更改参数(尽管在这种情况下不是很有用)
  • (可能)可以由编译器内联,而不会影响性能

<标准>库为什么使用类型标签而不是<代码>枚举>代码>来消除这些构造函数?< /强>也许更重要的是,在编写自己的C++代码时,我也更喜欢使用类型标签吗?

,要点是,如果您想使用<代码> EnUM < /C>您应该编辑
enum
,然后重新生成使用您的函数和
enum
的所有项目。此外,还有一个函数将enum作为参数,并使用
开关
或其他功能。这将给您的应用程序带来多余的代码


否则,如果将重载函数与标记一起使用,则可以轻松地添加另一个标记并添加另一个重载函数,而无需接触旧的标记。这是更向后兼容的。

使用这些标记可以利用语言的类型系统。这与模板元编程密切相关。简单地说,使用这些标记允许在编译时静态地做出关于调用哪个构造函数的分派决策。这为编译器优化留出了空间,提高了运行时效率,并使使用
std::unique_lock
的模板元编程更容易。这是可能的,因为标记是不同的静态类型。对于
enum
,这是无法做到的,因为在编译时无法预见
enum
的值。注意,使用标记进行区分是一种常见的模板元编程技术。只要看看标准库使用的那些就可以了。

我怀疑这是优化。请注意,使用类型(按原样)会在编译时选择正确的版本。正如您所指出的,在运行时,在某些条件语句(可能是
开关)中(可能)选择了使用
enum

在许多实现中,锁的获取和释放频率极高,设计者可能认为分支预测和隐含的内存同步事件可能是一个重要问题

我的论点中的缺陷(你也指出了)是构造函数很可能是内联的,而且条件很可能会被优化掉


请注意,使用“dummy”参数与实际提供命名构造函数最接近。

此方法称为标记分派(我可能错了)。具有不同值的枚举类型只是编译时的一种类型,不能使用枚举值重载构造函数。因此,对于enum,它将是一个包含switch语句的构造函数。标记分派相当于编译时的switch语句。每个标记类型都指定:这个构造函数将做什么,它将如何尝试获取锁。当您想在编译时做出决策时,应该使用类型标记,并在运行时使用enum做出决策。

标记调度 这是一种称为标记分派的技术。它允许根据客户机要求的行为调用适当的构造函数

使用标记的原因是,用于标记的类型因此是不相关的,并且在重载解析期间不会冲突。类型(而不是枚举中的值)用于解析重载函数。此外,标签还可以用来解析那些原本不明确的调用;在这种情况下,标记通常基于某些类型特征

带模板的标记调度意味着只需要实现给定构造所需使用的代码

标记分派允许更容易阅读代码(至少在我看来)和更简单的库代码;构造函数没有
switch
语句,在执行构造函数本身之前,可以根据这些参数在初始化器列表中建立不变量。当然,您的差异可能会有所不同,但这是我使用标签的一般经验

写了一篇关于标签分派技术的文章。它的使用历史似乎可以追溯到很久以前

为什么要用它? 为什么标准库使用类型标记而不是枚举来消除这些构造函数的歧义

在重载解析和可能的实现期间使用类型比枚举更强大、更灵活;请记住,枚举最初是无范围的,并且在如何使用它们方面受到限制(与标记相比)

标签的其他值得注意的原因

  • 编译时决策可以决定使用哪个构造函数,而不是运行时
  • 不允许更多“黑客”代码,其中整数被强制转换为枚举类型,其值不符合要求-需要制定设计决策来处理此问题,然后实现代码以满足任何结果异常或错误
  • 请记住,
    shared\u lock
    lock\u guard
    也使用这些标记,但在
    lock\u guard
    的情况下,仅使用
    采用锁
    。枚举将引入更多潜在的错误条件
我认为优先权和历史在这方面也发挥了作用。鉴于标准图书馆和其他地方的广泛使用;不太可能改变情况,
constexpr std::defer_lock_t defer_lock {};
constexpr std::try_to_lock_t try_to_lock {};
constexpr std::adopt_lock_t adopt_lock {};