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

时间:2009-09-19 08:11:54

标签: java c++ collections

在过去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段解释了如何为不同的迭代器类别编写函数的专用版本。

4 个答案:

答案 0 :(得分:6)

您可能想要考虑一个功能模板。查看一些std <algorithm>功能模板的工作原理,例如std::for_each

e.g。

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

然后,您可以使用多种可迭代范围调用从此模板生成的函数。

e.g。

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 :(得分: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.
   ....
}

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

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

答案 2 :(得分: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)和注释的一些描述性名称来完成的。

我还应该提到,如果你使用这个函数模板和不同类型的迭代器,那么多个“sum”函数将被“实例化”。如果您不希望这种代码重复和/或确实需要运行时多态性,您可以应用一种称为“类型擦除”的技术。但是,迭代器的类型擦除不是标准库的一部分。此外,我从未觉得有必要将此技术应用于迭代器。但是你会发现在其他库中使用类型擦除,比如boost :: any和boost :: function。

您可以使用其他一些模板技巧来区分不同的迭代器类别(请参阅“标签调度”)或约束您的功能模板(请参阅“SFINAE”)。如果你对类型擦除感兴趣,请尝试使用google搜索c ++,类型擦除,迭代器。您基本上创建一个句柄类来管理多态对象(通过指针)。这个多态对象包装了一些你想要“擦除”(隐藏)类型的其他对象。

答案 3 :(得分:-2)

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

所以在上面的例子中,你可能想要重写这个函数:

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

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