为什么std :: bitset实例中的std :: swap of Bits不起作用?

时间:2010-01-05 00:05:06

标签: c++

在下面的例子中,我期望比特的交换。相反,第二位会被覆盖,但为什么以及如何实现预期的行为?

#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"?
}

3 个答案:

答案 0 :(得分:14)

这是非常讨厌的。首先,我们必须查看交换声明:

template<class T>
void swap(T &left, T &right);

现在,operator[]()上的bitset有两个重载:

bool operator[](size_type _Pos) const;
reference operator[](size_type _Pos);

此处referencebitset::referencebitset中的嵌套类,有效地充当对其中一个底层位的代理引用。它封装的内容是bitsetbitset中的位置。由于swap的声明,选择了第二个重载,我们正在交换两个bitset::reference。现在这里变得讨厌。我们来看看swap的典型实现:

template class<T> swap(T &left, T &right) {
    T temp = left;
    left = right;
    right = temp;
}

问题是leftright都是对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分配给aa复制位置b所指的位值,并将其存储在tmp所指的位位置。最后,将b分配到tmp。但请记住,a仍然指的是tmp所指的位。阅读a与阅读swap相同,所以如果a = b; b = a; 只是这两行,你最终会得到同样的效果:

a

在您的代码中,b为0且{{1}}为1,因此使用这两个赋值语句,11就是我们期望看到的内容。