标准库类模板std::bitset<N>
有一个构造函数(C++11 及更高版本,unsigned long
C++11 之前的参数)
constexpr bitset(unsigned long long) noexcept
与许多最佳实践指南相反,此单参数构造函数未标记为explicit
. 这背后的原因是什么?
标准库类模板std::bitset<N>
有一个构造函数(C++11 及更高版本,unsigned long
C++11 之前的参数)
constexpr bitset(unsigned long long) noexcept
与许多最佳实践指南相反,此单参数构造函数未标记为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
由于std::bitset<N>
它的意思是unsigned int
对unsigned int
. 制作构造函数explicit
会破坏很多现有代码(现在添加它同样会破坏很多现有代码)。
更新:做一些标准考古,我发现1995 年 1 月的N0624explicit
建议在标准库草案之前的所有单参数构造函数中添加 then 全新的关键字。1995 年 3 月(奥斯汀)的一次会议上对此进行了表决。正如N0661中所记录的,unsigned long
构造函数 forbitset
没有做出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==
andoperator!=
中。这些成员函数在它们的 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
,是在具有非类型模板参数的功能模板之前。当时唯一可行的解决方法是让所有重载的运算符成为成员函数,而不是非成员函数。当更高级的模板技术可用时,这从未改变过(另请参阅此问答了解为什么这可能会破坏代码)。