在下面的例子中,我期望比特的交换。相反,第二位会被覆盖,但为什么以及如何实现预期的行为?
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
int main()
{
bitset<2> test(string("10"));
cout << test; // Prints "10"
swap(test[0], test[1]);
cout << test; // Prints "11", why not "01"?
}
答案 0 :(得分:14)
这是非常讨厌的。首先,我们必须查看交换声明:
template<class T>
void swap(T &left, T &right);
现在,operator[]()
上的bitset
有两个重载:
bool operator[](size_type _Pos) const;
reference operator[](size_type _Pos);
此处reference
是bitset::reference
,bitset
中的嵌套类,有效地充当对其中一个底层位的代理引用。它封装的内容是bitset
和bitset
中的位置。由于swap
的声明,选择了第二个重载,我们正在交换两个bitset::reference
。现在这里变得讨厌。我们来看看swap的典型实现:
template class<T> swap(T &left, T &right) {
T temp = left;
left = right;
right = temp;
}
问题是left
和right
都是对bitset::reference
的引用。它们具有相同的基础数据(因为它们是代理;同样意味着两者都指向相同的bitset
!)它们只是封装了bitset
中的不同位置。因此,请将其视为left
在某些bitset
中的位置0,right
在某些bitset
中的位置1,bitset
是相同的bitset
left
1}}为bitset
!我们永远将此BS
称为T temp = left;
(故意选择)。
所以,
temp
表示BS
位于left = right;
中的位置0。
BS
将位置0左侧设置为temp
中的位置1(同时更改right = temp;
中的位置0!)
BS
将位置1设置为BS
中的位置0(刚刚设置为namespace std {
template<>
void swap<bitset<2>::reference>(
bitset<2>::reference &left,
bitset<2>::reference &right
) {
bool temp = (bool)left;
left = (bool)right;
right = (bool)temp;
}
}
中的位置1!)。因此,在这个混乱结束时,位置0是1位置,位置1不变!现在,因为位置0是LSB而位置1是MSB,所以“10”变为“11”。难看。
您可以使用template specialization:
解决此问题int main() {
bitset<2> test(string("10"));
cout << test; // Prints "10"
swap(test[0], test[1]);
cout << test; // Prints "01", hallelujah!
}
然后:
{{1}}
答案 1 :(得分:2)
实际上,由于test[i]
返回一个bitset引用rvalue,我真的不明白swap
如何在这里编译。我的编译器(g ++ 4.3.3)告诉我:
test.cpp:12: error: no matching function for call to
'swap(std::bitset<2u>::reference, std::bitset<2u>::reference)'
/usr/include/c++/4.3/bits/stl_move.h:80: note: candidates are:
void std::swap(_Tp&, _Tp&) [with _Tp = std::bitset<2u>::reference]
答案 2 :(得分:2)
C ++中没有值类型来表示单个位,因此当您使用[]
运算符访问位集的元素时,您得到的是一个代理对象作为您请求的位的别名。分配给该代理对象会更改原始位集对象中的相应位值。
如Victor's answer所示,您的代码无法使用GCC进行编译。但是我们假设对swap
的调用会编译。你会得到类似这样的代码:
void swap(std::bitset<2>::reference& a, std::bitset<2>::reference& b)
{
std::bitset<2>::reference tmp = a;
a = b;
b = tmp;
}
tmp
声明使用a
初始化变量,但这不会复制该位。相反,它会复制代理对象,因此tmp
将引用到a
引用的同一位集中的相同位。下一行将b
分配给a
,a
复制位置b
所指的位值,并将其存储在tmp
所指的位位置。最后,将b
分配到tmp
。但请记住,a
仍然指的是tmp
所指的位。阅读a
与阅读swap
相同,所以如果a = b;
b = a;
只是这两行,你最终会得到同样的效果:
a
在您的代码中,b
为0且{{1}}为1,因此使用这两个赋值语句,11就是我们期望看到的内容。