简体   繁体   English

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

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

I've been a Java programmer almost exclusively for the past 8 years or so, and recently I've been playing with C++ again. 在过去8年左右的时间里,我几乎一直是Java程序员,最近我又一直在玩C ++。 Here's an issue that I've come up against with regards to iterators in C++ STL and Java. 对于C ++ STL和Java中的迭代器,我遇到了一个问题。

In Java, you can write a method that takes an iterator like this: 在Java中,您可以编写一个采用如下迭代器的方法:

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

You pass in an Iterator and the method does not need to know what the underlying collection of that iterator is, which is good. 你传入一个Iterator ,该方法不需要知道迭代器的底层集合是什么,这是好的。

In C++, there is no common base class for iterators (as far as I know). 在C ++中,迭代器没有通用的基类(据我所知)。 I'd have to write a function like this: 我必须写一个这样的函数:

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

In other words, some_function knows that the iterator is an iterator over a vector . 换句话说, some_function知道迭代器是vector的迭代器。 That's not good, because I want the function to work regardless of what the underlying collection of the iterator is. 这不好,因为无论迭代器的底层集合是什么,我都希望函数能够工作。

How can I do this in C++? 我怎么能用C ++做到这一点? If it isn't really possible, then what is the best way to create a function in C++ that takes a collection as a parameter, but that doesn't need to know what the exact kind of collection is? 如果真的不可能,那么在C ++中创建一个以集合作为参数的函数的最佳方法是什么,但是不需要知道集合的确切类型是什么?

Addendum 附录

Thanks for the answers. 谢谢你的回答。 In addition to the answers I found some good information on this in paragraph 7.5 (Iterator Traits) of the book The C++ Standard Library: A Tutorial and Reference (by Nicolai M. Josuttis). 除了答案之外,我在“C ++标准库:教程和参考” (Nicolai M. Josuttis)一书第7.5段(迭代器特征)中找到了一些很好的信息。 Paragraph 7.5.1 explains how to write specialized versions of functions for different iterator categories. 第7.5.1段解释了如何为不同的迭代器类别编写函数的专用版本。

You probably want to consider a function template. 您可能想要考虑一个功能模板。 Look at how some of std <algorithm> function templates work such as std::for_each . 看看一些std <algorithm>函数模板是如何工作的,比如std::for_each

eg 例如

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

You can then call a function generated from this template with many kinds of iterable ranges. 然后,您可以使用多种可迭代范围调用从此模板生成的函数。

eg 例如

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 );

Its best to indicate through a naming convention the kind of iterator and subsequently the kind of properties the iterator is required to posses. 最好通过命名约定来指示迭代器的类型以及迭代器所需的属性类型。 Below are some common naming conventions for iterators: 以下是迭代器的一些常见命名约定:

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

Below is a generic definition for sequence type containers, which include vector and deque. 下面是序列类型容器的通用定义,包括vector和deque。

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

This is an example of one of the big differences between C++ and Java. 这是C ++和Java之间的一个重大差异的例子。 The only abstraction tool Java has is runtime polymorphism (interfaces and abstract classes). Java唯一的抽象工具是运行时多态(接口和抽象类)。 In C++ you're not limited to that. 在C ++中,你不仅限于此。 You can create aliases for types and let classes have other associated/nested types. 您可以为类型创建别名,并让类具有其他关联/嵌套类型。 It lets you get away without runtime polymorhism in many cases. 在许多情况下,它可以让您在没有运行时多态性的情况下逃脱。 The compile-time kind of genericity has the advantage of being quite fast (no virtual function calls, inlining possibilities). 编译时类通用性具有非常快的优点(没有虚函数调用,内联可能性)。 Also, it eases life-time management when you don't have a garbage collector. 此外,当您没有垃圾收集器时,它可以简化生命周期管理。 You can simply create the objects on the stack. 您只需在堆栈上创建对象即可。

Here's an (untested) example: 这是一个(未经测试的)示例:

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

Here, "Iter" ist just a name. 在这里,“Iter”只是一个名字。 It doesn't actually impose any constraints on the type. 它实际上并没有对类型施加任何约束。 In case you want to instantiate this template with a type that is not an iterator (at least in a structural sense) you'll get a compile-time error (compile-time duck typing). 如果您想要使用不是迭代器的类型(至少在结构意义上)实例化此模板,您将收到编译时错误(编译时鸭子类型)。 So, part of your job is documenting what kind of type you are expecting. 因此,您的部分工作是记录您期望的类型。 This is usually done by picking some descriptive names of template parameters (ie ForwardIterator) and comments. 这通常通过选择模板参数(即ForwardIterator)和注释的一些描述性名称来完成。

I should also mention that multiple "sum" functions are going to be "instantiated" if you use this function template with varying kinds of iterators. 我还应该提到,如果你使用这个函数模板和不同类型的迭代器,那么多个“sum”函数将被“实例化”。 If you don't want this kind of code duplication and/or really need runtime polymorphism you can apply a technique called "type erasure". 如果您不希望这种代码重复和/或确实需要运行时多态性,您可以应用一种称为“类型擦除”的技术。 Type erasure for iterators is not part of the standard library, though. 但是,迭代器的类型擦除不是标准库的一部分。 Also, I never felt the need to apply this technique for iterators. 此外,我从未觉得有必要将此技术应用于迭代器。 But you'll find the use of type erasure in other libraries like boost::any and boost::function. 但是你会发现在其他库中使用类型擦除,比如boost :: any和boost :: function。

There are a couple of other template tricks you can use to differentiate between different iterator categories (see "tag dispatching") or constrain your function template (see "SFINAE"). 您可以使用其他一些模板技巧来区分不同的迭代器类别(请参阅“标签调度”)或约束您的功能模板(请参阅“SFINAE”)。 If you're interested in type erasure try googling for c++, type erasure, iterator. 如果你对类型擦除感兴趣,请尝试使用google搜索c ++,类型擦除,迭代器。 You basically create a handle class that manages a polymorphic object (through a pointer). 您基本上创建一个句柄类来管理多态对象(通过指针)。 This polymorphic object wraps some other object whose type you want to "erase" (hide). 这个多态对象包装了一些你想要“擦除”(隐藏)类型的其他对象。

You can use the header file and specify the minimum requirements that the iterator must support. 您可以使用头文件并指定迭代器必须支持的最低要求。

So in the above example, you might want to rewrite the function as such: 所以在上面的例子中,您可能希望重写函数:

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

for something that requires to be able to only move the iterator forward (++) through a collection. 对于需要只能通过集合向前移动迭代器(++)的东西。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM