将范围传递给 C++ 时,如何处理 iterator/const_iterator 不匹配问题?算法?
我正在开发的项目的部分源代码负责压缩一系列“事件”,如下所示:
#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
}
这里的问题是 compressAndCopyEatenEvents 函数有一个非-常量事件列表;此列表操作系统传递给 eatEvents
方法,该方法采用 const 引用并生成 const_iterator
。现在 compressAndCopyEatenEvenst 函数想要复制吃掉事件的范围,因此它决定使用某种算法(这里是 std::copy ,这当然也可以)被替换为正确的 std::list 构造函数调用 - 重点是这个问题存在于所有类型的范围中)。
不幸的是(?)许多(如果不是全部?)范围需要由相同的迭代器类型组成。然而,在上面的代码中,'e.begin()' 产生一个 EventList::iterator
(因为该对象不是 const),但 'newEnd' 是一个 EventList::const_iterator
代码>.
这里是否存在设计缺陷导致了这种混乱?你会如何应对?
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; this list os passed to the eatEvents
methods, which takes a reference-to-const and yields a 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).
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
.
Is there a design weakness here which causes this mess? How would you tackle it?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
在 C++03 中,唯一可能的方法是强制转换。 (这很丑,这是一个设计缺陷,是的)。
或者有另一个命名变量:
在 C++11 中,您有
cbegin
和cend
函数(始终返回const_iterator
),因此您可以这样做简单地In C++03 the only possible way is to cast. (which is ugly, which is a design flaw, yes).
or have another named variable:
In C++11 you have
cbegin
andcend
functions (that always returnconst_iterator
s) so you'd do simply考虑使用
这将导致调用正确的
list::begin()
重载,并让std::copy
干净地编译。Consider using
This will cause the correct
list::begin()
overload to be called, and forstd::copy
to compile cleanly.看看大师怎么说:
Scot Meyers in effective STL
第 27 项。使用 distance 和 advance 将容器的 const_iterator 转换为迭代器。
有效的是前进和距离
See what the master says:
Scot Meyers in Effective STL
Item 27. Use distance and advance to convert a container's const_iterators to iterators.
What works is advance and distance
您可以添加 eatEvents 的第二个重载,这样编译器将自动选择正确的重载,以保留常量性:(
其中一个或两个可以是非虚拟的,并以单个底层函数的形式实现。)
有时,效果很好,尽管我不相信这是完美的。
You could add a second overload of eatEvents, so the compiler will automatically pick the right one so as to preserves const-ness:
(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.