Tem*_*Rex 9 c++ explicit-constructor unsigned-long-long-int bitset implicit-conversion
标准库类模板std::bitset<N>有一个构造函数(C++ 11及更高版本,unsigned longC++ 11之前的参数)
constexpr bitset(unsigned long long) noexcept
Run Code Online (Sandbox Code Playgroud)
与许多最佳实践指南相反,此单参数构造函数未标记为explicit.这背后的理由是什么?
针对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
Run Code Online (Sandbox Code Playgroud)
由于std::bitset<N>表示的一般化unsigned int,因此构造函数可能被隐式设置,以利于基于raw适应现有的C样式位扭曲代码unsigned int。使构造函数explicit破坏了很多现有代码(现在添加它同样会破坏很多现有代码)。
更新:做一些标准考古,我发现1995年1月的N0624建议explicit在标准库之前的草稿中为所有单参数构造函数添加新的关键字。在1995年3月的会议(奥斯丁)上将其付诸表决。如N0661中所述unsigned long,bitset未进行构造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
Run Code Online (Sandbox Code Playgroud)
可以通过建议重载运算符来支持混合模式位旋转来解决此问题:
// @ 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)
Run Code Online (Sandbox Code Playgroud)
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
}
Run Code Online (Sandbox Code Playgroud)
这种行为的起源来自1992年的提案N0128。该提案的时机很大程度上锁定了未来的功能,该时机std::bitset早于具有非类型模板参数的功能模板。当时唯一可行的解决方法是使所有重载的运算符都成为成员函数,而不是非成员函数。后来,当更高级的模板技术可用时,这一点再也没有改变(有关可能破坏代码的原因,另请参阅本问答)。