2010-08-11 101 views
10

哎,這是一個長的標題。重載[]運算符在C++模板類與const/nonconst版本

這是我的問題。我有一個C++的模板類,並且我重載了[]運算符。我都一個const和非const版本,與非const版本通過引用返回,以便在類物品可以改變這樣:直到我用一個布爾值作爲

myobject[1] = myvalue; 

這一切工作模板參數。下面是顯示錯誤一個完整的例子:

#include <string> 
#include <vector> 
using namespace std; 

template <class T> 
class MyClass 
{ 
    private: 
     vector<T> _items; 

    public: 

     void add(T item) 
     { 
      _items.push_back(item); 
     } 

     const T operator[](int idx) const 
     { 
      return _items[idx]; 
     } 

     T& operator[](int idx) 
     { 
      return _items[idx]; 
     } 

}; 


int main(int argc, char** argv) 
{ 
    MyClass<string> Test1;  // Works 
    Test1.add("hi"); 
    Test1.add("how are"); 
    Test1[1] = "you?"; 


    MyClass<int> Test2;   // Also works 
    Test2.add(1); 
    Test2.add(2); 
    Test2[1] = 3; 


    MyClass<bool> Test3;  // Works up until... 
    Test3.add(true); 
    Test3.add(true); 
    Test3[1] = false;   // ...this point. :(

    return 0; 
} 

錯誤是編譯器錯誤和消息是:

error: invalid initialization of non-const reference of type ‘bool&’ from a temporary of type ‘std::_Bit_reference’ 

我讀的時候,發現STL使用了一些臨時數據類型,但我不明白爲什麼它除了布爾之外的其他所有功能。

任何幫助,將不勝感激。

+1

閱讀這篇文章:http://www.gotw.ca/publications/mill09.htm – 2010-08-11 13:42:18

+0

它可以是固定的。見下文。你只需要返回運算符[]返回的相同類型(通常是T&(但並不總是如向量) – 2010-08-11 14:09:42

回答

9

因爲vector<bool>專門從事STL,實際上並不符合標準容器的要求。一個GOTW文章中關於它的

香草薩特會談:http://www.gotw.ca/gotw/050.htm

+0

令人驚歎。我一直在使用STL多年,從未遇到過這種情況。感謝上帝,我把矢量課抽象掉了,否則我現在會處在一個受到傷害的世界裏。感謝你的回答。 – riwalk 2010-08-11 13:47:26

+0

全部都是如此,並解釋了爲什麼它不像上面描述的那樣工作。但是這並不意味着上面的代碼是不可修復的。只有當你開始嘗試獲取任何特定元素的地址時,這些問題才真正出現,而這些元素開始分崩離析(這裏不是這種情況)。 – 2010-08-11 14:23:47

+0

我認爲更大的問題是,正如那篇文章所提到的那樣,STL根據輸入分支它的行爲。這意味着根據輸入行爲可能會有很大的不同。我認爲編碼非常差。 – riwalk 2010-08-11 14:57:21

5

A vector<bool>沒有像所有其他向量一樣實施,也不像其他向量那樣工作。你最好不要使用它,也不要擔心你的代碼不能處理它的許多特性 - 它通常被認爲是一件壞事,由一些不知情的C++標準委員會成員強加給我們。

+0

即使標準委員會成員很快就意識到了他們的錯誤,並試圖刪除,但這是標準的性質,它卡在那裏...... – 2010-08-11 14:19:29

6

一個vector<bool>是不是一個真正的容器。您的代碼正在有效地嘗試將引用返回到單個位,這是不允許的。如果您將容器更改爲deque,我相信您會得到您期望的行爲。

+0

+1他會:)! – 2010-08-11 13:52:33

+0

+1 deque就像一個魅力:) – riwalk 2010-08-11 14:03:47

4

你的班級的一些單調變化應該修復它。

template <class T> 
class MyClass 
{ 
    private: 
     vector<T> _items; 

    public: 

     // This works better if you pass by const reference. 
     // This allows the compiler to form temorary objects and pass them to the method. 
     void add(T const& item) 
     { 
      _items.push_back(item); 
     } 

     // For the const version of operator[] you were returning by value. 
     // Normally I would have returned by const ref. 

     // In normal situations the result of operator[] is T& or T const& 
     // But in the case of vector<bool> it is special 
     // (because apparently we want to pack a bool vector) 

     // But technically the return type from vector is `reference` (not T&) 
     // so it you use that it should compensate for the odd behavior of vector<bool> 
     // Of course const version is `const_reference` 

     typename vector<T>::const_reference operator[](int idx) const 
     { 
      return _items[idx]; 
     } 

     typename vector<T>::reference operator[](int idx) 
     { 
      return _items[idx]; 
     } 
}; 
+0

+1找到一個實際的解決方案:)。我可能不會走這條路,只是因爲我不喜歡向後彎曲以滿足第三方圖書館的細微差別。我正在撰寫摘要STL的類,因此用戶應該無法在API中看到STL中剩餘的工件:P。感謝您的輸入! – riwalk 2010-08-11 14:59:46

1

作爲其他的答案指出,一個專門被提供在載體<布爾>的情況下,以優化空間分配。

但是,如果您使用vector :: reference而不是T &,仍然可以使您的代碼有效。事實上,在引用由STL容器持有的數據時,使用container :: reference是一個好習慣。

T& operator[](int idx) 

變得

typename vector<T>::reference operator[](int idx) 

當然療法中也是常量引用一個typedef:

const T operator[](int idx) const 

和這一個變爲(除去無用額外副本)

typename vector<T>::const_reference operator[](int idx) const 
1

錯誤的原因是vector<bool>專門用於打包存儲在其中的布爾值,並且vector<bool>::operator[]返回某種可讓您訪問該值的代理。

我不認爲一種解決方案將返回與vector<bool>::operator[]相同的類型,因爲那樣你就會將遺憾的特殊行爲複製到您的容器。

如果你想使用vector爲基礎類型留着,我相信布爾問題可以通過使用vector<MyBool>,而不是當MyClass進行實例化bool進行修補。

這可能是這樣的:

#include <string> 
#include <vector> 
using namespace std; 

namespace detail 
{ 
    struct FixForBool 
    { 
     bool value; 
     FixForBool(bool b): value(b) {} 
     operator bool&() { return value; } 
     operator const bool&() const { return value; } 
    }; 

    template <class T> 
    struct FixForValueTypeSelection 
    { 
     typedef T type; 
    }; 

    template <> 
    struct FixForValueTypeSelection<bool> 
    { 
     typedef FixForBool type; 
    }; 

} 

template <class T> 
class MyClass 
{ 
    private: 
     vector<typename detail::FixForValueTypeSelection<T>::type> _items; 

    public: 

     void add(T item) 
     { 
      _items.push_back(item); 
     } 

     const T operator[](int idx) const 
     { 
      return _items[idx]; 
     } 

     T& operator[](int idx) 
     { 
      return _items[idx]; 
     } 

}; 


int main(int argc, char** argv) 
{ 
    MyClass<string> Test1;  // Works 
    Test1.add("hi"); 
    Test1.add("how are"); 
    Test1[1] = "you?"; 


    MyClass<int> Test2;   // Also works 
    Test2.add(1); 
    Test2.add(2); 
    Test2[1] = 3; 


    MyClass<bool> Test3;  // Works up until... 
    Test3.add(true); 
    Test3.add(true); 
    Test3[1] = false;   // ...this point. :(

    return 0; 
} 
+0

優雅......我可能不會使用它(僅僅因爲我沒有向量的附件 - 這個類正在抽象矢量),但是如果使用向量,這是一個優雅的解決方案。 – riwalk 2010-08-11 16:34:08

+0

雖然我自己有第二個疑問。我仍然不會建議用'&my_vec [0]'來取地址。 – UncleBens 2010-08-11 18:13:34

+0

使用STL中元素的地址與使用STL中的迭代器一樣有風險。任何時候迭代器失效(通過添加一個新元素),該地址可能會失效。無論哪種方式,這個示例類只是包裝vector <>類。通過這樣做,它只是傳遞與vector <>類相同的風險級別。 – riwalk 2010-08-11 18:39:35