【问题标题】:C++11 and [17.5.2.1.3] Bitmask TypesC++11 和 [17.5.2.1.3] 位掩码类型
【发布时间】:2012-03-25 01:37:05
【问题描述】:

标准允许人们在整数类型、enumstd::bitset 之间进行选择。

考虑到这些选择,为什么库实现者会使用其中一个?

例如,llvm 的 libcxx 似乎使用了(至少)以下两个实现选项的组合:

ctype_base::mask 使用整数类型实现: <__locale>

regex_constants::syntax_option_type 使用 enum + 重载运算符实现: <regex>

gcc项目的libstdc++全部使用了三个

ios_base::fmtflags 使用枚举 + 重载运算符实现:<bits/ios_base.h>

regex_constants::syntax_option_type 使用整数类型实现, regex_constants::match_flag_type 是使用 std::bitset 实现的
两者:<bits/regex_constants.h>

AFAIK,gdb 无法“检测”这三个选项中任何一个的位域,因此增强调试不会有任何区别。

enum 解决方案和整数类型解决方案应始终使用相同的空格。 std::bitset 似乎并不能保证 sizeof(std::bitset<32>) == std::uint32_t 所以我看不出 std::bitset 有什么特别吸引人的地方。

enum 解决方案的类型安全性似乎稍差一些,因为掩码的组合不会生成枚举器。

严格来说,上述内容是针对 n3376 而不是 FDIS(因为我无权访问 FDIS)。

我们将不胜感激。

【问题讨论】:

  • N3376 只是 C++11 标准的修订版,它修复了一些小的编辑问题。内容没有区别。
  • 我很清楚,只是想清楚一点。感谢您对这些链接的帮助:)
  • 我想你已经知道了,但我认为它值得重复:通常情况下,枚举类型可以保存比它们的枚举数集更多的值,所以二进制操作可以很好他们。事实上,确保它工作更容易,因为我们可以知道指定一个显式的底层类型。
  • 无论标准如何保证这些替代方案中的任何一个,实现者都可以向bitset 添加更多整数类型、枚举类型功能或附加约束。但这些类型与效率无关,因此不太可能做出任何努力。

标签: c++ enums c++11 bitmask libc++


【解决方案1】:

真正令人惊讶的是,该标准将其限制为仅三种选择。为什么不应该接受类类型?总之……

  • 整数类型是最简单的替代方法,但它们缺乏类型安全性。非常旧的遗留代码往往会使用这些,因为它们也是最古老的。
  • 枚举类型安全但繁琐,在 C++11 之前,它们倾向于固定为 int 的大小和范围。
  • std::bitset 可能具有更多的类型安全性,因为 bitset<5>bitset<6> 是不同的类型,不允许添加,但在其他方面不安全,就像整数类型一样。如果他们允许来自std::bitset<N> 的类型派生,这将不是问题。

显然enums 是理想的选择,但经验证明类型安全确实没有必要。因此,他们向实施者扔了一块骨头,让他们采取更简单的路线。那么,简短的回答是懒惰导致实施者选择intbitset

不允许从 bitset 派生的类型有点奇怪,但实际上这是一件小事。

该子句提供的主要规范是在这些类型上定义的一组操作(即按位运算符)。

【讨论】:

  • 标准中没有任何地方说这些类型仅限于这三种选择。这些只是OP正在谈论的三个。至于类型安全是不必要的,这肯定是一个事实没有证据......
  • @NicolBolas 引用的段落说“每个位掩码类型都可以实现为重载某些运算符的枚举类型、整数类型或位集 (20.5)。”这听起来像是只有三种选择的独家清单。
  • @NicolBolas 如果不同类型标志之间的类型安全很重要,那么库就不会为它们使用整数类型。但是OP举了这样一个例子。所以,是的,证据表明对此并不重视。你需要冷静一下。
  • 什么应该限制为这三种类型?我们甚至知道我们在说什么吗?
【解决方案2】:

我的偏好是使用枚举,但有时使用整数是有正当理由的。通常ctype_base::mask 与本机操作系统标头交互,从ctype_base::mask 映射到<ctype.h> 实现定义的常量,例如_CTYPE_L_CTYPE_U 用于isupperislower 等。使用整数可能会使 ctype_base::mask 直接与本机 OS API 一起使用更容易。

我不知道为什么 libstdc++ 的 <regex> 使用 std::bitset。提交该代码时,我在心里记下了用枚举替换整数类型,但<regex> 不是我工作的优先事项。

【讨论】:

    【解决方案3】:

    为什么标准允许以不同的方式实现库?答案是:为什么不呢?

    如您所见,这三个选项显然都在某些实现中使用。如果可以避免的话,该标准不希望使现有的实现不符合标准。

    使用位集的一个原因可能是它的大小比枚举或整数更适合。并非所有系统甚至都有std::uint32_t。也许bitset<24> 在那里会更好用?

    【讨论】:

    • 另一个问题是为什么要混搭,这对我来说没有多大意义。这也没有解释 enum 与新实现使用 libcxx 的整数类型。虽然std::uint32_t 确实不存在于一个平台上,但uint_least32_t 保证存在于所有平台上。我无法想象这也是一个合理的原因,因为他们都没有在基于enum 的位掩码上使用: uint_least8_t
    • 在 gcc 的情况下,库的不同部分是由不同的人在不同的时间编写的。这可能会导致不同的选择。我敢打赌ios_base 的设计至少比regex 早了10 年。即使uint_least32_t 存在于所有平台上,我们也不知道它的大小。有时它可能会映射到 unsigned long long,这可能有点过头了。
    • 我同意 bitset 点,因为它是一个实现选项。如果 std::bitset 是使用 char 类型的数组实现的,但它在 libstdc++-v3 中使用 long 类型的数组,这可能是合理的
    猜你喜欢
    • 2016-02-20
    • 2012-08-17
    • 1970-01-01
    • 1970-01-01
    • 2010-09-23
    • 2018-09-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多