代码之家  ›  专栏  ›  技术社区  ›  Christian Ammer

为什么std::bitset实例中的位交换不起作用?

c++
  •  10
  • Christian Ammer  · 技术社区  · 16 年前

    在下面的示例中,我期望交换位。相反,第二个位将被覆盖,但为什么以及如何才能实现预期的行为?

    #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 回复  |  直到 16 年前
        1
  •  14
  •   Cassio Neri    12 年前

    这真是令人讨厌。首先,我们要看一下掉期申报:

    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 ,中的嵌套类 位组 它实际上充当对其中一个底层位的代理引用。它封装的是 位组 以及在 位组 . 因为声明 swap ,选择第二个重载,我们将交换两个 位集::引用 S.现在这里是它变得讨厌的地方。让我们看看交换的典型实现:

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

    问题是 left right 是否都引用了 位集::引用 . 它们具有相同的基础数据(因为它们是代理;相同的意思是两者指向相同 位组 !)它们只是把不同的位置封装在一起 位组 .因此,这样想 左边 位置0在某些位置 位组 正确的 位置1在某些位置 位组 位组 是一样的 位组 作为 左边 !让我们永远参考这个 位组 作为 BS (有意选择)。

    所以,

    T temp = left;
    

    temp 位置0在 BS .

    left = right;
    

    将左侧的位置0设置为位置1 BS (同时改变位置0 临时雇员 !)

    right = temp;
    

    将位置1向右设置为位置0 in BS (刚刚设置到位置1 BS !)所以在混乱的最后,位置0是位置1之前的任何位置,位置1不变!现在,因为位置0是LSB,位置1是MSB,所以“10”变为“11”。丑陋的

    你可以用一个 template specialization :

    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;
        }
    }
    

    然后:

    int main() {
        bitset<2> test(string("10"));
        cout << test; // Prints "10"
        swap(test[0], test[1]);
        cout << test; // Prints "01", hallelujah!
    }
    
        2
  •  2
  •   Community Mohan Dere    9 年前

    C++中没有值类型来表示单个位,所以当使用 [] 访问位集元素的运算符,得到的是 代理对象 作为您请求的位的别名。分配给该代理对象会更改原始位集对象中相应的位值。

    AS 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 但这并不能复制位。相反,它生成代理对象的副本,因此 川芎嗪 在同一个位集中的相同位 是指。下一行指定 b ,从位置复制位值 引用并存储在位位置 是指。最后,还有 川芎嗪 进入之内 . 但请记住 川芎嗪 还是指那个位 是指。阅读 川芎嗪 和阅读一样 所以你最终会得到同样的效果如果 掉期 只是这两行:

    a = b;
    b = a;
    

    在您的代码中, 是0和 是1,所以对于这两个赋值语句,11正是我们希望看到的。

        3
  •  2
  •   Cassio Neri    12 年前

    实际上,因为 test[i] 返回一个位集引用右值,我不太明白 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]