简体   繁体   English

将范围传递给C ++算法时,如何处理iterator / const_iterator不匹配?

[英]How do you handle iterator/const_iterator mismatches when passing ranges to C++ algorithms?

A part of the source code for a project I'm working on, which is responsible for compressing a sequence of 'events', looks like this: 我正在处理的项目的一部分源代码,它负责压缩一系列“事件”,如下所示:

#include <iterator>
#include <list>

typedef int Event;

typedef std::list<Event> EventList;

struct Compressor {
    // Returns an iterator behind the last element which was 'eaten'
    virtual EventList::const_iterator eatEvents( const EventList &l ) = 0;
};

// Plenty of Compressor subclasses exist

void compressAndCopyEatenEvents( Compressor &c ) {
    EventList e;
    e.push_back( 1 );
    EventList::const_iterator newEnd = c.eatEvents( e );

    EventList eatenEvents;
    std::copy( e.begin(), newEnd, std::back_inserter( eatenEvents ) ); // barfs
}

The issue here is that the compressAndCopyEatenEvents function has a non-const list of events; 这里的问题是compressAndCopyEatenEvents函数具有事件的非常量列表。 this list os passed to the eatEvents methods, which takes a reference-to-const and yields a const_iterator . 此列表传递给eatEvents方法,该方法采用对const的引用并产生const_iterator Now the compressAndCopyEatenEvenst function would like to copy the range of eaten events away, so it decides to use some algorithm ( std::copy here, which of course could just as well be replaced with the right std::list constructor call - the point is that this problem exists with all kinds of ranges). 现在compressAndCopyEatenEvenst函数想将吃掉的事件范围复制掉,因此它决定使用某种算法( std::copy在这里,当然也可以用正确的std::list构造函数调用代替该算法)是所有范围都存在此问题)。

Unfortunately(?) many (if not all?) ranges need to be composed from the same iterator type. 不幸的是,许多(如果不是全部?)范围需要由相同的迭代器类型组成。 However, in the above code, 'e.begin()' yields an EventList::iterator (because the object is not const) but 'newEnd' is an EventList::const_iterator . 但是,在上面的代码中,'e.begin()'产生一个EventList::iterator (因为该对象不是const),但是'newEnd'是一个EventList::const_iterator

Is there a design weakness here which causes this mess? 这里是否存在导致此混乱的设计缺陷? How would you tackle it? 您将如何解决?

In C++03 the only possible way is to cast. 在C ++ 03中,唯一可能的方法是强制转换。 (which is ugly, which is a design flaw, yes). (这很丑,这是设计缺陷,是的)。

std::copy( static_cast<EventList::const_iterator>e.begin(), newEnd, std::back_inserter( eatenEvents ) );

or have another named variable: 或具有另一个命名变量:

EventList::const_iterator constBegin = e.begin();
std::copy(constBegin , newEnd, std::back_inserter( eatenEvents ) );

In C++11 you have cbegin and cend functions (that always return const_iterator s) so you'd do simply 在C ++ 11你cbegincend功能(即总是返回const_iterator S)所以你根本就

std::copy( e.cbegin(), newEnd, std::back_inserter( eatenEvents ) );

Consider using 考虑使用

EventList::const_iterator b = e.begin();
std::copy( b, newEnd, std::back_inserter( eatenEvents ) );

This will cause the correct list::begin() overload to be called, and for std::copy to compile cleanly. 这将导致调用正确的list::begin()重载,并使std::copy干净地编译。

See what the master says: 看看主人怎么说:

Scot Meyers in Effective STL 有效STL中的Scot Meyers

Item 26. Prefer iterator to const iterator, reverse_iterator, and const_reverse_iterator. 项目26.首选迭代器而不是const迭代器,reverse_iterator和const_reverse_iterator。 Though containers support four iterator types, one of those types has privileges the others do not have. 尽管容器支持四种迭代器类型,但是其中一种类型具有特权,而其他类型则没有。 That type is iterator, iterator is special. 该类型是迭代器,迭代器是特殊的。 迭代器类型之间存在的转换。

typedef deque<int> IntDeque; //STL container and
typedef lntDeque::iterator Iter; // iterator types are easier
typedef lntDeque::const_iterator ConstIter; // to work with if you
// use some typedefs
Iter i;
ConstIter ci;
… //make i and ci point into
// the same container
if (i == ci ) ... //compare an iterator
// and a const_iterator

Item 27. Use distance and advance to convert a container's const_iterators to iterators. 项27.使用距离和前进将容器的const_iterators转换为迭代器。

typedef deque<int> IntDeque; //convenience typedefs
typedef lntDeque::iterator Iter;
typedef lntDeque::const_iterator ConstIter;
ConstIter ci; // ci is a const_iterator
…
Iter i(ci); // error! no implicit conversion from
// const_iterator to iterator
Iter i(const_cast<Iter>(ci)); // still an error! can't cast a
// const_iterator to an iterator

What works is advance and distance 有效的是前进和距离

typedef deque<int> IntDeque; //as before
typedef IntDeque::iterator Iter;
typedef IntDeque::const_iterator ConstIter;
IntDeque d;
ConstIter ci;
… // make ci point into d
Iter i(d.begin()); // initialize i to d.begin()
Advance(i, distance(i, ci)) //move i up to where ci is
// (but see below for why this must
// be tweaked before it will compile)

You could add a second overload of eatEvents, so the compiler will automatically pick the right one so as to preserves const-ness: 您可以添加eatEvents的第二个重载,因此编译器将自动选择正确的重载,以保持const-ness:

virtual EventList::iterator eatEvents( EventList &l ) = 0;

(One or both of them could be non-virtual and implemented in terms of a single underlying function.) (它们中的一个或两个可以是非虚拟的,并且可以根据单个基础功能来实现。)

Sometimes that works well, although I'm not convinced it's the perfect thing here. 有时效果很好,尽管我不认为这是完美的选择。

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

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