簡體   English   中英

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

[英]Why the std::swap of Bits in a std::bitset instance doesn't work?

在下面的示例中,我希望交換位。 相反,第二位被覆蓋,但為什么以及如何實現預期的行為?

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

這是非常討厭的。 首先我們要看看掉貨聲明:

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

現在, bitset上的operator[]()有兩個重載:

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

這里referencebitset::reference ,在嵌套類bitset ,有效地充當代理引用到底層的比特之一。 它所封裝是bitset ,並在一個位置bitset 由於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是在某些位置0 bitsetright是在某些位置1 bitsetbitset相同bitsetleft 讓我們永遠將這個bitset稱為BS (故意選擇)。

所以,

T temp = left;

tempBS位置0。

left = right;

將位置0設置在BS中的左側位置1(同時在temp更改位置0!)

right = temp;

設置在右側位置0位置1 BS (這是剛剛成立到位置1 BS !)。 因此,在這個混亂結束時,位置0是1位置,位置1不變! 現在,因為位置0是LSB而位置1是MSB,所以“10”變為“11”。 丑陋。

你可以通過模板專業化解決這個問題:

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

實際上,由於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]

C ++中沒有值類型來表示單個位,因此當您使用[]運算符訪問位集的元素時,您得到的是一個代理對象 ,它充當您請求的位的別名。 分配給該代理對象會更改原始bitset對象中的相應位值。

正如Victor的回答所示,您的代碼無法使用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是0, b是1,所以使用這兩個賦值語句,11正是我們期望看到的。

#include <bitset>
#include <assert.h>
#include <stdio.h>
using namespace std;


int main()
{
    bitset<128> bs(42);
    bs[11]=0;
    bs[12]=1;
    assert(bs[12]==1);
    printf("bs[11]=%d\n", (int)bs[11]);
    printf("bs[12]=%d\n", (int)bs[12]);
    int bsi = bs[11], bsj=bs[12];
    swap(bsi, bsj);
    bs[11]=bsi;
    bs[12]=bsj;
    printf("bs[11]=%d\n", (int)bs[11]);
    printf("bs[12]=%d\n", (int)bs[12]);
    return 0;
}

在此處輸入圖像描述

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM