2012-09-03 73 views
3

我有點新C++, 我有一個抽象類(純虛擬),其中2類從它繼承。 每個派生類保存類似的數據,只在不同的STL容器(數組vs地圖) 我重載基類的運算符,以執行,例如,添加STL元素。 我希望能夠迭代STL的元素,而不管它是什麼類型。多態的通用STL迭代器

我已經搜索了幾天,現在泛型迭代器,和類似的東西,但找不到任何東西。

我沒有太多的代碼可以分享,因爲我無法做任何事情。 我想也許讓我的基類擁有一個模板變量,它將代表STL容器,然後在運算符中獲取它的迭代器,但是又不知道如何去做。

protected: 
    template<class T> 
    T gStlContainer; 

,然後訪問

gStlContainer::iterator<double> it; 

顯然沒有奏效。

有什麼建議嗎? 謝謝!

編輯: 我正在編輯嘗試使用示例更好地解釋。 我重載了基類(抽象)類中的+運算符,我希望它執行的是迭代STL容器的每個元素,並將其添加到另一個元素,例如, 假設我將這個數組作爲STL

arr = [0,1,2,3] // Say it's stored in Derived2 class 
arr = [4,5,6,7] // Say it's stored in Derived3 class 

這些數組每個存儲在一個派生類中。 當我在做

Derived1 = Derived2 + Derived3; 

然後Derived1將舉行

arr = [4,6,8,10] 

希望它更清楚一點了。問題在於它並不總是一個數組,例如它可以組合數組和地圖。這就是爲什麼我需要泛型迭代器或某種解決方案。

謝謝!

+0

什麼是您的基類的界面(顯示您想要的樣子的一個有限版本)。我會考慮在基類中定義接口,然後實現一個單一的模板化派生類型,該類型將容器的類型作爲模板參數...還要注意您必須使用哪種版本的標準(C++ 03/C++ 11)以及是否可以使用類似'boost :: function'或'std :: function'的庫。如果不需要提供算法的單個實現並且每個派生類型都進行迭代,那麼它可能再簡單得多。 –

回答

1

C++沒有泛型迭代器,但它的模板函數可以寫入任何類型的迭代器。爲了添加一個容器中的所有元素,已經有了一個算法給你。閱讀關於std::accumulate

+0

謝謝你的回答,但是我很抱歉,我不明白那是怎麼幫助我的? 此外,我編輯我的問題,以嘗試和更好地解釋我後 –

1

您從錯誤的角度看待問題。你可以做到這一點,但你爲什麼?如果它的行爲不同,你的基類不應該實現該方法,而應該將邏輯委託給派生類。

class Base 
{ 
public: 
    virtual ~Base() {} 
    virtual void iterate() = 0; 
}; 

class Derived : Base 
{ 
public: 
    virtual void iterate() { /*iterate through vector or map or whatever*/ } 
}; 
+0

感謝您的答案,我需要迭代的函數是一個運算符重載,我不想超載每個案件(即,向數組添加數組,向數組添加數組等) 我編輯了我的問題以包含更具體的示例 –

+0

@Lablabla邏輯錯誤。哪個元素應該映射[1]'返回?地圖不是連續的... –

+0

map [i]映射int-> double應該添加到數組[i],如果map [i]不存在,則將其視爲零。但稍後會有所改變。練習描述表明我們應該以這種方式實現它,而不用關心STL容器。那裏特別提到地圖。 –

0

明確的答案是:你不能,至少不能用STL的迭代器。

迭代器通常構建爲模板,並且編譯器會爲每個模板專業化創建一個新類型。你可以將vector :: iterator想象成VectorFloatIterator和map :: iterator作爲MapFloatIterator。

它們是不相關的類型 - 即使通過繼承 - 只是恰好有相似的接口,當然,C++不是鴨子類型。當兩種類型完全無關時,您無法將VectorFloatIterator傳遞給期望MapFloatIterator的函數。

這甚至適用於使用標準算法 - 例如for_each算法是模板化的,因此編譯器將創建類似於for_each_vector_float_iterator和for_each_map_float_iterator函數的函數,它們不能彼此使用。

在編譯時想到正在生成的類型是有幫助的。您在運行時將不同類型的函數傳遞給函數會發生,當時間太晚。你只能在編譯時使用C++模板的「泛化」(是的,我寫了這個詞)。

這就是說,@Luchian Grigore對這個問題有最認可的答案:專門處理operator+來處理任何可能出現在附加聲明中的各種類型。這是一種痛苦嗎?是的,有點。

也就是說,boost::any就是您要查找的用例 - 它們是類似STL的容器,可以使用模板處理各種類型的而不是

1

動態多態對於對象來說是合理的,但對於算法來說它確實非常糟糕。對於算法,使用靜態多態性會更好。如果你覺得你想在它們之間有一個支持operator+()的容器系統,只要確保它們以某種方式引用一個名字空間並在這個名字空間中定義一個合適的operator+()。例如,您可以使用std::vector<T, A>,分配器A從具有運算符的名稱空間中繼承。以下是這種方法的一個例子。

這個想法基本上是讓名稱空間實現合適的操作符。例如,addable實施operator+()printable實施operator<<()。除非類型以某種方式引用它們,否則不會查看這些命名空間。通過繼承此名稱空間中的類型或使用繼承這些類型之一的類型的模板參數。這種看起來機制被稱爲參數依賴查找。因此,這兩個命名空間都提供了一個空的struct tag {};,這些空間在被繼承時不需要花費任何費用。

爲了獲得容器提到這些命名空間的方式,下面的代碼只是創建了一個分配器類模板,它們都源自這兩種類型。然後將此分配器與std::vector<T, A>std::list<T, A>一起使用來創建類型別名,以輕鬆創建相應的容器。爲了展示一切都很好,main()只是演示了運營商的使用。

下面的代碼利用了C++ 2011在幾個地方的優點,使得表示法更短一些。該原理也適用於C++ 2003。主要使用類型別名和初始化程序列表不起作用。

#include <algorithm> 
#include <functional> 
#include <iostream> 
#include <iterator> 
#include <list> 
#include <vector> 

namespace addable 
{ 
    struct tag {}; 

    template <typename T0, typename T1> 
    T0 operator+ (T0 const& c0, T1 const& c1) 
    { 
     T0 rc; 
     std::transform(c0.begin(), c0.end(), 
         c1.begin(), 
         std::back_inserter(rc), 
         std::plus<typename T0::value_type>()); 
     return rc; 
    } 
} 

namespace printable 
{ 
    struct tag {}; 
    template <typename T, typename = typename T::value_type> 
    std::ostream& 
    operator<< (std::ostream& out, T const& value) 
    { 
     out << "["; 
     if (!value.empty()) { 
      std::copy(value.begin(), value.end() - 1, 
         std::ostream_iterator<typename T::value_type>(out, ", ")); 
      out << value.back(); 
     } 
     return out << "]"; 
    } 
} 

template <typename T> 
struct my_allocator 
    : addable::tag 
    , printable::tag 
    , std::allocator<T> 
{ 
}; 

template <typename T> 
using my_vector = std::vector<T, my_allocator<T>>; 
template <typename T> 
using my_list = std::vector<T, my_allocator<T>>; 

int main() 
{ 
    my_vector<int> v({ 1, 2, 3, 4 }); 
    my_list<int> l({ 2, 3, 4, 5 }); 
    my_vector<int> rc = v + l; 

    std::cout << v << " + " << l << " = " << (v + l) << "\n"; 
}