为啥带有 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<N>
是unsigned int
的泛化,构造函数可能是隐式的,以方便基于原始unsigned int
调整现有的C 风格的位旋转代码。构造构造函数explicit
会破坏很多现有代码(现在添加它同样会破坏很多现有代码)。
更新:做一些标准考古,我发现 N0624 从 1995 年 1 月开始,建议在 pre-Standard Library 中的所有单参数构造函数中添加当时全新的关键字 explicit
草案。 1995 年 3 月(奥斯汀)的一次会议上对此进行了表决。如 N0661 中所述,bitset
的 unsigned long
构造函数不是 explicit
(一致投票,但没有动机)。
混合模式位旋转
然而,尽管bitset
很容易从unsigned long
初始化,否则存在不完整的混合模式集合操作(&
、|
或^
):
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 & 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