为啥带有 unsigned long long 参数的 std::bitset 构造函数未标记为显式?

Posted

技术标签:

【中文标题】为啥带有 unsigned long long 参数的 std::bitset 构造函数未标记为显式?【英文标题】:Why is the std::bitset constructor with an unsigned long long argument not marked as explicit?为什么带有 unsigned long long 参数的 std::bitset 构造函数未标记为显式? 【发布时间】:2014-11-08 02:45:02 【问题描述】:

标准库类模板std::bitset<N> 有一个构造函数(C++11 及更高版本,unsigned long C++11 之前的参数)

constexpr bitset(unsigned long long) noexcept 

与许多最佳实践指南相反,此单参数构造函数未标记为 explicit。这背后的原理是什么?

【问题讨论】:

C++ 标准库有相当一部分有问题的设计决策。 @n.m.当然,所以我正在寻找一个答案来确定我是否应该为此提交一份缺陷报告;-) 作为一个猜测,因为运算符没有被重载以接受无符号整数,这样mybitset |= 0x4; 之类的代码应该可以编译。 可能std::bitset<32> bs = 0x0FFFFFFF; 进行编译是有意义的。无论如何,我怀疑他们是否会在这一点上明确说明 - 破坏了太多代码。 【参考方案1】:

显式构造

反对explicit 构造函数的主要反对意见是从无符号整数复制初始化不再有效

constexpr auto N = 64;
std::bitset<N> b(0xDEADC0DE);  // OK, direct initialization
std::bitset<N> b = 0xDEADC0DE; // ERROR, copy initialization cannot use explicit constructors

由于std::bitset&lt;N&gt;unsigned int 的泛化,构造函数可能是隐式的,以方便基于原始unsigned int 调整现有的C 风格的位旋转代码。构造构造函数explicit 会破坏很多现有代码(现在添加它同样会破坏很多现有代码)。

更新:做一些标准考古,我发现 N0624 从 1995 年 1 月开始,建议在 pre-Standard Library 中的所有单参数构造函数中添加当时全新的关键字 explicit草案。 1995 年 3 月(奥斯汀)的一次会议上对此进行了表决。如 N0661 中所述,bitsetunsigned long 构造函数不是 explicit(一致投票,但没有动机)。

混合模式位旋转

然而,尽管bitset 很容易从unsigned long 初始化,否则存在不完整的混合模式集合操作(​​&amp;|^):

 constexpr auto N = 512;
 std::bitset<N> b = 0xDEADC0DE; // OK
 std::bitset<N> c = b & 0xFFFF; // ERROR, cannot deduce template arguments for rhs

这可以通过提出重载运算符来支持混合模式位旋转来解决:

 // @ from  &, |, ^ 

 template<std::size_t N> 
 bitset<N> operator@(unsigned long long lhs, const bitset<N>& rhs)

 template<std::size_t N> 
 bitset<N> operator@(const bitset<N>& lhs, unsigned long long rhs)

作为成员函数的重载运算符

std::bitset 在混合模式功能方面的精神分裂性质也存在于operator==operator!= 中。这些成员函数在其 rhs 参数上具有隐式转换,但在其 lhs 参数上没有(this 指针,它受模板参数推导的影响)。这导致以下结果:

#include <bitset>
#include <iostream>

int main()

    constexpr auto N = 64;
    constexpr std::bitset<N> b = 0xDEADC0DE; // OK, copy initialization

    std::cout << (b == 0xDEADC0DE);     // OK, implicit conversion on rhs
    std::cout << (0xDEADC0DE == b);     // ERROR, no implicit conversion on lhs

这种行为的起源源于 1992 年的提案 N0128。该提案的时间安排在很大程度上锁定了未来std::bitset 的功能,是在具有非类型模板参数的函数模板之前。当时唯一可行的解​​决方法是让所有重载的运算符成为成员函数,而不是非成员函数。当更高级的模板技术可用时,这从未改变过(另请参阅 this Q&A 了解这可能会破坏代码的原因)。

【讨论】:

b &amp; 0xFFFF doesn't work anyway.

以上是关于为啥带有 unsigned long long 参数的 std::bitset 构造函数未标记为显式?的主要内容,如果未能解决你的问题,请参考以下文章

带有 unsigned long long 和 sprintf 的 Visual C++ 6.0

为啥 ioctl 调用的原型使用 unsigned long 作为第三个参数?

在 CMSIS 中,为啥位域位置是“unsigned int”,而掩码基数是“unsigned long int”?

为啥我的 C 代码片段不起作用?简化版可以。为 unsigned long long 传递不带 VA_ARGS 的 args

在 C 中给出 unsigned long long 变量值的警告

如何打印大于“unsigned long long”的“std::bitset”的十进制值?