簡體   English   中英

如何編寫以通用方式獲取迭代器或集合的函數?

[英]How to write a function that takes an iterator or collection in a generic way?

在過去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 ++中創建一個以集合作為參數的函數的最佳方法是什么,但是不需要知道集合的確切類型是什么?

附錄

謝謝你的回答。 除了答案之外,我在“C ++標准庫:教程和參考” (Nicolai M. Josuttis)一書第7.5段(迭代器特征)中找到了一些很好的信息。 第7.5.1段解釋了如何為不同的迭代器類別編寫函數的專用版本。

您可能想要考慮一個功能模板。 看看一些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 );

最好通過命名約定來指示迭代器的類型以及迭代器所需的屬性類型。 以下是迭代器的一些常見命名約定:

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.
   ....
}

下面是序列類型容器的通用定義,包括vector和deque。

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

這是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)和注釋的一些描述性名稱來完成。

我還應該提到,如果你使用這個函數模板和不同類型的迭代器,那么多個“sum”函數將被“實例化”。 如果您不希望這種代碼重復和/或確實需要運行時多態性,您可以應用一種稱為“類型擦除”的技術。 但是,迭代器的類型擦除不是標准庫的一部分。 此外,我從未覺得有必要將此技術應用於迭代器。 但是你會發現在其他庫中使用類型擦除,比如boost :: any和boost :: function。

您可以使用其他一些模板技巧來區分不同的迭代器類別(請參閱“標簽調度”)或約束您的功能模板(請參閱“SFINAE”)。 如果你對類型擦除感興趣,請嘗試使用google搜索c ++,類型擦除,迭代器。 您基本上創建一個句柄類來管理多態對象(通過指針)。 這個多態對象包裝了一些你想要“擦除”(隱藏)類型的其他對象。

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

所以在上面的例子中,您可能希望重寫函數:

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

對於需要只能通過集合向前移動迭代器(++)的東西。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM