2013-05-14 73 views
3

簡而言之,如果我有一個集合和向量,我該如何創建一個可以同時處理兩個參數的泛型方法。如何迭代C++中的集合?

我想要做的就是迭代兩種類型的集合。聽起來像它應該是微不足道的,但我失去了一些東西。

void printMeSomeStrings(somebaseclass<string> strings) { 
    for (auto& str : strings) { 
    cout << str << endl; 
    } 
} 

在C#中,我會通過IEnumerable或類似的東西。然後我可以遍歷集合。

任何一般解讀解釋答案將不勝感激。

回答

1

第一個選項是把代碼做一個模板的迭代。這要求將實施暴露給使用它的每個人,這有缺點。

基本上,參數爲C作爲template參數,然後根據該類型C編寫代碼。

template<typename C> 
void printMeSomeStrings(C&& strings) { 
    for (auto const& str : strings) { 
    cout << str << endl; 
    } 
} 

如果你希望能夠有接口和實現之間有很強的阻隔,在C++ 11的辦法是搞類型擦除在for -iterable容器,然後暴露for -iterable容器,就像std::function的工作方式一樣。

這更棘手。我個人認爲編寫一個for_each函數比編寫一個完整的迭代適配器更容易。如果您想要完整的容器迭代類型擦除對象,請從boost開始,或者在下面問我,我可能會這樣做。

但是,for_each適配器很容易。

#include <functional> 
#include <utility> 
#include <iterator> 
#include <memory> 

template<typename T> 
struct for_each_helper_interface { 
    virtual ~for_each_helper_interface() {} 
    virtual void for_each(std::function< void(T) > const&) = 0; 
}; 
template<typename C, typename T> 
struct for_each_helper:for_each_helper_interface<T> { 
    C& c; 
    for_each_helper(C& in):c(in) {} 
    virtual void for_each(std::function< void(T) > const& f) override final { 
    for(auto&& x:c) { 
     f(x); 
    } 
    } 
}; 
template<typename T> 
struct for_each_adaptor { 
    std::unique_ptr<for_each_helper_interface<T>> pImpl; 
    void for_each(std::function< void(T) > const& f) { 
    if (pImpl) { 
     pImpl->for_each(f); 
    } 
    } 
    template<typename C> 
    for_each_adaptor(C&& c): pImpl(new for_each_helper<C, T>(std::forward<C>(c))) {} 
}; 

將類型擦除T容器(或類型轉換爲T!),並公開一個for_each方法,可以讓你遍歷容器的內容。使用這樣的:

#include <set> 
#include <iostream> 
#include <vector> 
void print_stufF(for_each_adaptor<std::string const&> c) { 
    c.for_each([&](std::string const&s){ 
    std::cout << s << "\n"; 
    }); 
} 
int main() { 
    std::set<std::string> s; 
    s.insert("hello"); 
    s.insert("world"); 
    print_stuff(s); 
    std::vector<std::string> v; 
    v.push_back("hola"); 
    v.push_back("bola"); 
    print_stuff(v); 
} 

這到底是怎麼回事的是,對於所使用的每個類型來構建我們的適配器,我們建立的每個自定義實現。然後,我們存儲一個指向此自定義類的抽象基類的指針,併爲每個調用重定向它。

這意味着什麼專業std::begin或定義自己的開始不需要相關:我們創建專用關係,而不是在使用點。

活生生的例子:http://ideone.com/xOqBkI

+1

Boost已經擁有'any_range',它將執行類似於'std :: function'的類型擦除。 – 2013-05-15 12:22:27

4

您可以使用模板。例如:

#include <iostream> 

template<typename C> 
void foo(C const& c) 
{ 
    std::cout << "{ "; 
    for (auto const& x : c) 
    { 
     std::cout << x << " "; 
    } 
    std::cout << "}"; 
} 

這裏是你將如何使用它:

#include <set> 
#include <vector> 

int main() 
{ 
    std::vector<int> v = {1, 2, 3}; 
    foo(v); 

    std::cout << std::endl; 

    std::set<std::string> s = {"Hello,", "Generic", "World!"}; 
    foo(s); 
} 

Live example.

+0

一個不錯的答案,如果你想避免定義自己的模板,你可以使用std :: for_each與lambda或打印機函數。 – Alex 2013-05-14 22:29:21

3

這是正是什麼迭代器被設計爲。

template <class It> 
void print_some_strings(It first, It last) { 
    std::cout << *first++ << std::endl; 
} 
0

在C#中,我會通過IEnumerable的或類似的東西。

C++使用鴨式打字的更多pythonic方法來定義接口(通常稱爲C++中的概念),而不是使用繼承。做鴨子類型在C++中,你使用模板函數是這樣的:

template<typename C> 
void printMeSomeStrings(const C& strings) 
{ 
    for (const auto& str : strings) 
    { 
     cout << str << endl; 
    } 
} 

在Python中,鴨子類型是在運行時完成的,但在C++中,它是在編譯時完成的,因此不存在運行成本鴨打字,並且在編譯時檢查所有內容。

這是關於C++的更多信息,以幫助查找信息。首先,相當於IEnumerator<T>是C++中的迭代器。 Here是關於不同迭代器類別的頁面,以及迭代器需要實現的內容。出於傳統的原因,迭代器是在C中的指針之後建模的,這使您可以使用具有標準C++算法的C數組。

但是,與IEnumerator<T>不同,迭代器必須成對出現。開始和結束的迭代器(它是最後一個元素之後的迭代器)。因此,在C++中相當於IEnumerable<T>稱爲範圍。在C++ 11中,範圍由兩個自由函數begin(T)end(T)(它也可以作爲成員函數.begin().end()實現)定義。

通過將概念(aka接口)定義爲兩個自由函數,而不是使用繼承,範圍可以非侵入式地實現。舉例來說,如果你使用了一些使用C風格鏈接列表的遺留api。現在它們可以被改編爲C++ 11範圍,並在C++ for循環內部使用。

+0

這不是鴨子打字 - 這是類型推斷。 – 2014-02-03 10:10:41

+0

@MichaelBurge它在C++社區中通常被稱爲編譯時鴨子輸入。見[這裏](http://www.drdobbs.com/templates-and-duck-typing/184401971) – 2014-02-05 13:50:34