繁体   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