2009-09-06 71 views
6

我試圖在C創建一個「稀疏」載體類++,像這樣:重載操作符[]爲稀疏向量

template<typename V, V Default> 
class SparseVector { 
    ... 
} 

在內部,它會被一個std::map<int, V>(表示,其中V是存儲的值類型)。如果地圖中沒有元素,我們將假設它等於來自模板參數的值Default

但是,我無法重載下標操作符[]。我必須重載[]運算符,因爲我將此類中的對象傳遞給了一個Boost函數,該函數預計[]能正常工作。

const版本很簡單:檢查索引是否在映射中,如果是,則返回其值,否則爲Default

但是,非const版本要求我返回一個引用,這就是我遇到麻煩的地方。如果該值僅爲,則爲,我不需要(也不想)向地圖添加任何內容;但如果是寫成,我可能需要在地圖中添加一個新條目。問題是,超載[]不知道是否值讀取寫入。它僅僅返回一個參考。

有什麼辦法可以解決這個問題嗎?或者也許要解決它?

+2

boost :: mapped_vector <>應該做類似的事情 - 你可以研究它的想法(或者只是使用它)。 – 2009-09-06 17:10:19

+0

它不支持我的默認值,而且,我打算爲二維矩陣做這件事,所以直接使用它是不可能的。但仍然是一個有用的參考! – Thomas 2009-09-06 19:08:09

回答

13

可能有一些非常簡單的技巧,但除此之外,我認爲operator[]只需返回可以從V分配的東西(並轉換爲V),而不一定是V &。所以我認爲你需要返回一個超載的對象operator=(const V&),它會在你的稀疏容器中創建條目。

您必須檢查Boost函數對其模板參數的作用,但用戶定義的轉換到V會影響可能的轉換鏈,例如通過阻止同一個用戶定義的轉換鏈。

+0

這是唯一的解決方案。另一方面,代理對象也不完美 - 例如,如果'V'有任何重載的轉換運算符,則代理將無法無縫地傳播它們。 – 2009-09-06 17:22:32

+0

剛剛測試了助推矢量。看起來他們也是這樣。有趣。 – 2009-09-06 17:38:51

+0

(並且'vector '也不是STL容器。) – sbi 2009-09-06 22:22:54

9

不要讓非常量運算符&實現返回一個引用,而是一個代理對象。然後,您可以實現代理對象的賦值運算符,以區分從讀寫訪問到讀操作符[]的讀訪問。

下面是一些代碼草圖來說明這個想法。這種方法並不美觀,但很好 - 這是C++。 C++程序員不會浪費時間參加選美比賽(他們也不會有機會)。 ;-)

template <typename V, V Default> 
ProxyObject SparseVector::operator[](int i) { 
    // At this point, we don't know whether operator[] was called, so we return 
    // a proxy object and defer the decision until later 
    return ProxyObject<V, Default>(this, i); 
} 

template <typename V, V Default> 
class ProxyObject { 
    ProxyObject(SparseVector<V, Default> *v, int idx); 
    ProxyObject<V, Default> &operator=(const V &v) { 
     // If we get here, we know that operator[] was called to perform a write access, 
     // so we can insert an item in the vector if needed 
    } 

    operator V() { 
     // If we get here, we know that operator[] was called to perform a read access, 
     // so we can simply return the existing object 
    } 
}; 
+0

有什麼辦法讓一個類型可以轉換爲'const int&'和'int&',並且只要編譯器可以使用不可寫的左值,編譯器就會選擇它?我希望有一個代理對象返回一個引用到主對象加載的字段,然後讓它的析構函數將字段複製回主對象,如果它可能已經改變,但我不明白如何以避免在只讀訪問上回寫。 – supercat 2015-03-20 18:40:08

1

我想知道這個設計是否合理。

如果您想要返回一個引用,這意味着該類的客戶端可以將調用operator[]的結果存儲在引用中,並在以後的任何時間對其進行讀取/寫入。如果您沒有返回引用,並且/或者每次處理特定索引時都不插入元素,他們怎麼能這樣做呢? (另外,我有這樣的感覺,即標準需要一個合適的STL容器來提供operator[]來讓該操作符返回一個引用,但我不確定這一點。)

你也許可以通過給你的代理也提供一個operator V&()(它會創建條目並指定默認值),但我不確定在某些情況下這不會打開另一個循環孔我還沒有想過。

std::map通過指定該操作者的非const版本總是插入一個元素(並且不提供const版本在所有)解決了這個問題。

當然,你可以隨時說這個不是一個現成的STL容器,並且operator[]不會返回用戶可以存儲的純引用。也許這沒關係。我只是好奇。

+0

關於設計的可靠性:它是一個通用向量的內存優化,用於特定情況 - 所以接口/合同/文檔應該覆蓋該內容。關於引用的有效性:即使stl迭代器也不總是有效的。 – xtofl 2009-09-06 20:11:54

+2

@xtofl:但是,STL容器非常仔細地定義了哪些操作可以使迭代器和/或對容器元素的引用無效。這個容器應該這樣做,並且用戶必須確保使用該類作爲參數的任何模板都不會提出它不能滿足的要求。 – 2009-09-06 20:37:59