我有一个按需生成值的迭代器DataIterator
,因此取消引用运算符返回一个Data,而不是Data&。我认为这是一件好事,直到我试图通过将数据包装在reverse_iterator中来反转数据DataIterator。
DataCollection collection
std::reverse_iterator<DataIterator> rBegin(iter) //iter is a DataIterator that's part-way through the collection
std::reverse_iterator<DataIterator> rEnd(collection.cbegin());
auto Found = std::find_if(
rBegin,
rEnd,
[](const Data& candidate){
return candidate.Value() == 0x00;
});
当我运行上面的代码时,它永远不会找到一个值等于0的Data对象,即使我知道一个存在。当我在谓词中插入一个断点时,我会看到奇怪的值,我永远不会期望看到像0xCCCC - 可能是未初始化的内存。会发生什么是reverse_iterator的dereference运算符看起来像这样(来自xutility - Visual Studio 2010)
Data& operator*() const
{ // return designated value
DataIterator _Tmp = current;
return (*--_Tmp); //Here's the problem - the * operator on DataIterator returns a value instead of a reference
}
最后一行是问题所在 - 创建临时数据并返回对该数据的引用。该引用立即无效。
如果我将std :: find_if中的谓词更改为(数据候选者)而不是(const Data&amp; candidate),那么谓词就可以了 - 但我很确定我只是幸运地遇到了未定义的行为。该引用无效,但我在内存遭到破坏之前正在复制数据。
我该怎么办?
我可以对DataIterator做些什么,以防止其他人在6个月后尝试同样的事情时,用半天时间弄清楚出了什么问题?
答案 0 :(得分:1)
并不是说我非常喜欢这个主意,但是如果你分配了一个Data
对象,然后将一个引用返回到shared_ptr
,这将允许外部世界保持如果需要,可以更长时间地使用它,当你向前迈进时,你就会“忘记”它。
另一方面,实施自己的原生reverse_iterator
可能是一个更大的胜利。这就是我为自己的链表做的事情,因为我没有使用像gcc
那样的标记对象而且不能使用std::reverse_iterator
。这真的不是那么难。
答案 1 :(得分:1)
这是因为reverse_iterator
接口是在decltype
存在之前设计的。今天,那将写成
auto operator*() const -> decltype(*current)
{ // return designated value
DataIterator _Tmp = current;
return (*--_Tmp);
}
和in C++14,甚至不需要尾随返回类型,因为可以推断它。
decltype(auto) operator*() const
{ // return designated value
DataIterator _Tmp = current;
return (*--_Tmp);
}
答案 2 :(得分:0)
我最后在评论中提到了凯西的建议。他没有把它作为我能接受的答案发布,所以我会自己写出来。
我对DataIterator的reverse_iterator进行了专门化,返回值而不是引用。这涉及从xutility复制/粘贴实现,指定一个模板参数作为DataIterator,并更改
reference operator*() const
到
value operator*() const