【问题标题】:C++: boost range iterator pointing to wrong elementC ++:提升范围迭代器指向错误的元素
【发布时间】:2013-09-19 11:43:21
【问题描述】:

我遇到了一个奇怪的问题。我有一个vector<pair<bool, int>>,我只需要从中读取(并且可能写入)该对的布尔值为真的向量元素。我正在使用升压范围滤波器和反向适配器来做到这一点。

但是,我注意到适配器的顺序,即我是否使用颠倒 |过滤或过滤 |反转会产生不同的结果。事实上,当我使用过滤 |反过来,当我使用一个迭代器到转换后的范围来更改该对的布尔值时,更改后的迭代器指向不同的向量元素。当我使用 reversed | 时不会发生这种情况过滤。下面是演示该问题的代码。任何关于为什么会发生这种情况的想法都非常感谢!

#include <boost/range/adaptors.hpp>
#include <vector>
#include <utility>
#include <iostream>

using namespace boost::adaptors;

using container_type = std::vector<std::pair<bool,int>>;

struct to_include {
  bool operator()(const std::pair<bool,int>& x) {
    return x.first;
  }
};

int main() {
  container_type container;

  /* element0: 1, 1 */
  /* element1: 1, 2 */
  /* element2: 1, 3 */
  for(size_t i=0; i!=3; ++i) container.push_back(std::make_pair(true, i+1));
  container_type container_cpy = container;

  /* filter and then reverse */
  auto fr = container | filtered(to_include()) | reversed;
  auto fr_it1 = fr.begin();
  auto fr_it2 = std::next(fr_it1);
  fr_it2->first = false;

  std::cout << "FILTER AND THEN REVERSE\n";
  std::cout << fr_it2->first << " " << fr_it2->second << '\n'; /* prints (1,1) instead of (0,2) */

  /* reverse and then filter */
  auto rf = container_cpy | reversed | filtered(to_include());
  auto rf_it1 = rf.begin();
  auto rf_it2 = std::next(rf_it1);
  rf_it2->first = false;

  std::cout << "\nREVERSE AND THEN FILTER\n";
  std::cout << rf_it2->first << " " << rf_it2->second << '\n'; /* prints (0,2) */

  return 0;
}

【问题讨论】:

  • 我承认我没有使用升压范围适配器的经验,但如果 fr 是某种 reverse 范围对我来说是有意义的,所以 fr_it1 应该是rbegin 而不是begin
  • @us2012:感谢您的帮助。 fr 是一个反转的范围(实际上是一个反转的过滤范围),这意味着它的begin 迭代器指向原始容器的最后一个元素(即反转之前)。

标签: c++ boost iterator range adaptor


【解决方案1】:

这是一个微妙的问题。这里的重点是,在你修改了fr_it2 指向的元素之后,你也隐式地修改了fr_it1,因为fr 是对原始范围的惰性视图。这意味着需要重新计算transformed 过滤器。这是一个非常不直观的属性,因为对于急切的 STL 范围,通过迭代器进行的修改不会修改迭代器本身,但对于惰性范围,这不再是真的!

事实上,如果您使用“新鲜”迭代器打印整个frrf 范围,您会发现它们的内容实际上是相同的。

fr_it2->first = false;
for (auto e : fr) std::cout << e.first << e.second << ";"; // prints 13;11
...
rf_it2->first = false;
for (auto e : rf) std::cout << e.first << e.second << ";"; // prints 13;11

Live Example 1。所以实际上中间元素确实被删除了!

我认为你不应该通过迭代器将元素修改到适应的范围,而是通过迭代器到你的主容器中,像这样:

auto fr_it1 = container.begin();
...
auto rf_it1 = container_cpy.begin();

Live Example 2。 如果你这样做,你会得到一致的结果,两种方法都显示“0 2”。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-06-11
    • 2019-09-19
    • 2013-05-14
    • 1970-01-01
    • 2018-08-02
    • 2013-07-08
    • 2018-04-16
    • 1970-01-01
    相关资源
    最近更新 更多