2009-09-19 61 views
2

我在過去的8年左右一直是Java程序員,最近我一直在玩C++。以下是我在C++ STL和Java中針對迭代器提出的問題。如何編寫以通用方式接受迭代器或集合的函數?

在Java中,你可以編寫需要這樣一個迭代的方法:

void someMethod(Iterator<String> data) { 
    // ... 
} 

傳入一個Iterator和方法不需要知道什麼是迭代器的底層集合是,這是好。

在C++中,沒有用於迭代器的公共基類(據我所知)。我不得不寫一個函數是這樣的:

void some_function(std::vector<std::string>::const_iterator data) { 
    // ... 
} 

換句話說,some_function知道迭代器是在vector的迭代器。這並不好,因爲我希望函數能夠工作,而不管迭代器的底層集合是什麼。

我該如何在C++中做到這一點?如果這不是真的可行,那麼在C++中創建一個以集合爲參數的函數的最佳方式是什麼,但不需要知道確切種類的集合是什麼?

附錄

感謝您的答案。除了答案之外,我在The C++ Standard Library: A Tutorial and Reference(Nicolai M. Josuttis着)的第7.5段(迭代器特徵)中找到了一些關於這方面的很好的信息。段落7.5.1解釋瞭如何爲不同的迭代器類別編寫函數的專用版本。

回答

2

它最好通過命名約定來指示迭代器的種類以及迭代器需要擁有的屬性類型。下面是一些共同的命名慣例迭代:

 
template<typename Iterator> 
void foo_iterator(Iterator begin, Iterator end) 
{ 
    typedef typename std::iterator_traits<Iterator>::value_type T; 
    .... 
} 

template<typename RandomIterator> 
void foo_random_iterator(RandomIterator begin, RandomIterator end) 
{ 
    typedef typename std::iterator_traits<RandomIterator>::value_type T; 
    .... 
} 

template<typename ForwardIterator> 
void foo_forward_iterator(ForwardIterator begin, ForwardIterator end) 
{ 
    typedef typename std::iterator_traits<ForwardIterator>::value_type T; 
    .... 
} 

template<typename ReverseIterator> 
void foo_forward_iterator(ReverseIterator begin, ReverseIterator end) 
{ 
    typedef typename std::iterator_traits<ReverseIterator>::value_type T; 
    .... 
} 

template<typename InputIterator> 
void foo_input_iterator(InputIterator begin, InputIterator end) 
{ 
    typedef typename std::iterator_traits<InputIterator>::value_type T; 
    .... 
} 

template<typename OutputIterator> 
void foo_output_iterator(OutputIterator out) 
{ 
    // We don't have a type T, as we can't "always" 
    // know the type, as this type of iterator is a sink. 
    .... 
} 

以下是序列類型容器,其中包括載體和雙端隊列通用定義。

 
template <typename T, 
      class Allocator, 
      template <class,class> class Sequence> 
inline void foo_sequence(Sequence<T,Allocator>& sequence) 
{ 
    .... 
} 
+1

謝謝,這看起來像一個很好的解決方案。 – Jesper 2009-09-19 08:57:50

6

你可能想要考慮一個函數模板。看一下std<algorithm>函數模板如何工作,如std::for_each

例如

template< class Iterator > 
void some_function(Iterator first, Iterator last) 
{ 
    // ... 
} 

然後,您可以使用多種可迭代範圍調用此模板生成的函數。

例如

std::vector<double> my_doubles; 
// ... populate doubles 
some_function(my_doubles.begin(), my_doubles.end()); 


std::set<Custom> my_custom_class_set; 
// ... populate ... 
some_function(my_custom_class_set.begin(), my_custom_class_set.end()); 

int raw_array[50]; 
// ... populate ... 
some_function(raw_array, raw_array + 50); 
+1

的關鍵在於,在C++中,模板在編譯時初始化,所以你不需要爲你的迭代器公共基類,只要它們都具有相同的「形」。 – 2009-09-19 08:18:25

+0

看看像for_each()這樣的標準算法是否是一個好主意,謝謝。 – Jesper 2009-09-19 08:55:01

-2

您可以使用header file並指定迭代器必須支持的最低要求。

所以在上面的例子中,你可能要重寫的功能,例如:

template<typename T> 
void some_function(std::forward_iterator<T> data) { 
    ... 
} 

的東西,要求能夠只(++),通過收集向前移動迭代器。

+0

「std」名稱空間中沒有'forward_iterator'。有一個'forward_iterator_tag',但這不是一個迭代器,它是從迭代器獲得的一條信息,它使用一個traits類。 – 2009-09-19 08:25:08

+0

這或多或少地將Java示例一對一轉換爲C++;像這樣的東西是我第一次嘗試,而且不起作用。 – Jesper 2009-09-19 08:32:27

+4

我的猜測是你想說: 模板 無效some_function(標準:迭代<性病:: forward_iterator_tag,T>吧) {} 這是一個不正確的做法,因爲難保傳入的迭代器是從std :: iterator派生的。 ATM定義迭代器的最好方法就是將其命名財產明智的,希望用理解。例如:InputIterator的,ForwardIterator,RandomIterator等等 希望在未來引入的概念時,事情會變得更加可口。 – 2009-09-19 08:35:35

2

這是C++和Java之間最大區別之一的例子。 Java唯一的抽象工具是運行時多態(接口和抽象類)。在C++中,你不僅限於此。您可以爲類型創建別名,並讓類具有其他關聯/嵌套類型。它可以讓你在很多情況下沒有運行時多態。編譯時類型的通用性具有相當快的優點(沒有虛函數調用,內聯可能性)。另外,當你沒有垃圾收集器時,它可以簡化生命週期管理。您可以簡單地在堆棧上創建對象。

下面是一個(未經測試)例如:

template<typename Iter> 
typename std::iterator_traits<Iter>::value_type 
sum(Iter begin, Iter end) { 
    typedef typename std::iterator_traits<Iter>::value_type vt; 
    vt accum = vt(); 
    while (begin!=end) { 
     accum += *begin; 
     ++begin; 
    } 
    return accum; 
} 

這裏, 「Iter項目」 北京時間只是一個名字。它實際上並沒有對該類型施加任何限制。如果你想用一個不是迭代器的類型實例化這個模板(至少在結構意義上),你會得到一個編譯時錯誤(編譯時鴨式鍵入)。所以,你的工作的一部分是記錄你期望的類型。這通常通過選擇一些模板參數(即ForwardIterator)和註釋的描述性名稱來完成。

我還要提到的是多個「和」功能將被「實例」,如果你使用這個函數模板與不同類型的迭代器。如果你不想要這種代碼重複和/或真的需要運行時多態性,你可以應用一種稱爲「類型擦除」的技術。不過,迭代器的類型擦除不是標準庫的一部分。另外,我從未覺得有必要將這種技術應用於迭代器。但是你會發現在其他類庫如boost :: any和boost :: function中使用類型擦除。

有一對夫婦的其他模板的技巧,你可以使用不同的迭代器種類來區分(見「標籤調度」)或限制你的函數模板(請參閱「SFINAE」)。如果您對類型刪除感興趣,請嘗試使用Google搜索功能,輸入erasure,iterator。你基本上創建了一個處理多態對象的句柄類(通過指針)。這個多態對象包裝了一些你想要「擦除」(隱藏)的其他對象。

+0

「編譯時鴨式打字」 - 是的,似乎沒有辦法指示value_type必須是特定類型,或者指定其他類型的約束。這是C++'概念'應該在C++的未來版本中添加的內容嗎? – Jesper 2009-09-19 21:33:41

+0

是的。概念是一種類型的類型系統。不幸的是,他們沒有把它變成C++ 0x。但是可以使用「模板技巧」來模擬使用本機概念支持所能做的一些事情 - 包括將函數模板限制爲某種類型的迭代器類型。 – sellibitze 2009-09-19 22:15:11

相關問題