【发布时间】:2014-01-13 23:50:39
【问题描述】:
我有一个迭代器DataIterator,它按需生成值,因此取消引用运算符返回一个数据,而不是数据&。我认为这是一件好事,直到我尝试通过将数据 DataIterator 包装在 reverse_iterator 中来反转数据。
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 的取消引用运算符看起来像这样(来自 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 中的谓词更改为采用 (Data Candidate) 而不是 (const Data& Candidate),则该谓词有效 - 但我很确定我只是对那里未定义的行为感到幸运。引用无效,但我会在内存被破坏之前制作数据副本。
我能做什么?
- 修复我的 DataIterator 以使 operator* 返回 Data& 而不是 Data?我真的不明白这怎么可能。我的 DataIterator 返回 Data 而不是 Data& 的全部意义在于我没有空间将整个未压缩数据集保存在内存中,因此我创建了您想要按需查看的项目。
也许我可以保留“当前”数据值 - 但是当您增加或减少 DataIterator 时,该引用将变得无效。编辑 one of the answers suggests a shared_ptr - 编写一个reverse_iterator 的特化并使其取消引用运算符返回一个值而不是引用?这似乎是一项令人沮丧的工作,但可以理解,因为我的 DataIterator 在这里表现不佳 - 而不是 STL 的其余部分。
- 按照同样的思路,也许可以创建一个反向的 find_if - 可能比专门化 reverse_iterator 工作更少。
- 其他我没想到的东西
我可以对 DataIterator 做些什么来防止其他人在 6 个月后尝试同样的事情时花半天时间找出问题所在?
【问题讨论】:
-
为什么要为这个任务选择迭代器?
-
将数据复制到
vector<Data>,并在该向量上使用reverse_iterator -
选择了一个迭代器,因为底层数据非常非常大,但是被压缩了。迭代器解压缩数据,在您迭代数据时一次一个项目。这让我们可以查看数据集,并懒惰地创建派生数据集,而无需在内存中保留多个非常大的数据集。
-
std::reverse_iterator要求其参数是双向迭代器 (C++11§[reverse.iter.requirements]/1)。您的迭代器充其量是一个输入迭代器,因为即使是低级的 Forward 迭代器也需要operator*才能评估为value_type&([forward.iterators]/1)。您必须编写自己的专业化reverse_iterator。 -
@PeteBaughman 这无疑是标准库中最严重的设计缺陷之一,就在 iostream 中将 I/O、格式化、国家化和字符编码耦合在一起。 Boost 的人在不久前做了很多工作来分离迭代器中的遍历和访问的概念(New Iterator Concepts),甚至有一些语言委员会的提案文件,但我认为它没有任何结果。
标签: c++ stl iterator reverse-iterator