如何编写以通用方式获取迭代器或集合的函数?

在过去8年左右的时间里,我几乎一直是Java程序员,最近我又一直在玩C ++。 对于C ++ STL和Java中的迭代器,我遇到了一个问题。

在Java中,您可以编写一个采用如下迭代器的方法:

void someMethod(Iterator data) { // ... } 

您传入一个Iterator ,该方法不需要知道该迭代器的底层集合是什么,这很好。

在C ++中,迭代器没有通用的基类(据我所知)。 我必须写一个这样的函数:

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

换句话说, some_function知道迭代器是vector的迭代器。 这不好,因为无论迭代器的底层集合是什么,我都希望函数能够工作。

我怎么能用C ++做到这一点? 如果真的不可能,那么在C ++中创建一个以集合作为参数的函数的最佳方法是什么,但是不需要知道集合的确切类型是什么?

附录

谢谢你的回答。 除了答案之外,我在“C ++标准库:教程和参考” (Nicolai M. Josuttis)一书的第7.5段(迭代器特征)中找到了一些很好的信息。 第7.5.1段解释了如何为不同的迭代器类别编写函数的专用版本。

最好通过命名约定来指示迭代器的类型以及迭代器所需的属性类型。 以下是迭代器的一些常见命名约定:

 template 
 void foo_iterator(迭代器开始,迭代器结束)
 {
    typedef typename std :: iterator_traits  :: value_type T;
    ....
 }

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

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

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

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

 template 
 void foo_output_iterator(OutputIterator out)
 {
    //我们没有T型,因为我们不能“永远”
    //知道类型,因为这种类型的迭代器是接收器。
    ....
 }

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

 template  class Sequence>
 inline void foo_sequence(Sequence &sequence)
 {
    ....
 }

您可能想要考虑一个function模板。 看看一些std 函数模板是如何工作的,比如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 ); 

这是C ++和Java之间的一个重大差异的例子。 Java唯一的抽象工具是运行时多态(接口和抽象类)。 在C ++中,您不仅限于此。 您可以为类型创建别名,并让类具有其他关联/嵌套类型。 在许多情况下,它可以让您在没有运行时多态性的情况下逃脱。 编译时类通用性具有非常快的优点(没有虚函数调用,内联可能性)。 此外,当您没有垃圾收集器时,它可以简化生命周期管理。 您只需在堆栈上创建对象即可。

这是一个(未经测试的)示例:

 template typename std::iterator_traits::value_type sum(Iter begin, Iter end) { typedef typename std::iterator_traits::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 void some_function(std::forward_iterator data) { ... } 

对于需要只能通过集合向前移动迭代器(++)的东西。