【问题标题】:Using boost_foreach without const_iterator在没有 const_iterator 的情况下使用 boost_foreach
【发布时间】:2011-10-26 08:48:28
【问题描述】:

有没有办法在不定义const_iterator 的情况下使用boost foreach?

我的用例是一个向量的迭代器,它可以包含无效元素。迭代器应该遍历向量,并且只产生有效元素。它还应该修复向量,因为它应该将每个无效项与下一个有效项交换,并在最后调整向量的大小。例如,如果 -1 表示无效值,则向量 [6,-1,-1,9,-1,2] 应迭代 6,9 和 2 并将向量保留为 [6,9,2]。

我尝试使用boost::iterator_facade 来实现它,但我想不出一种方法来实现const_iterator,因为向量可以通过删除无效值来改变,因此不能是const

【问题讨论】:

  • 迭代器本身不应该改变列表。算法改变列表是一回事。但是迭代器对象永远不应该这样做。
  • 感谢您的提示,我是 C++ 新手,但仍在为一些概念而苦苦挣扎。冒着跑题的危险:你能提出另一种实现这种行为的方法吗?我想在迭代期间进行修复,所以我不必第二次循环它们。

标签: c++ boost foreach iterator constants


【解决方案1】:

所有形式的“foreach”都专门用于迭代容器的每个元素。您不是只是遍历容器的每个元素。您在迭代时修改容器。

所以只需编写一个常规的 for 循环。不需要特别的聪明或任何东西。


这是它的代码:

std::vector<int> it = vec.begin();
for(; it != vec.end;)
{
  if(*it < 0)
  {
    it = vec.erase(it);
    continue;
  }
  else
  {
    //Do stuff with `it`.
    ++it;
  }
}

看,只是一个简单的循环。不需要花哨的迭代器外观或其他类似的噱头。

【讨论】:

  • 我确实需要一些技巧来删除无效条目。我想在迭代过程中删除它们,因此对集合的下一次迭代会更快。但是,是的,我看到了 foreach 遍历所有元素的问题。我认为 all elementsall valid elements 并且 container 是逻辑容器而不是实际向量。
  • @Flogo:查看我的编辑。那就是我的意思。哦,下一次迭代不会更快,因为您将不断检查是否有任何条目无效。这张支票不是免费的,即使您通常会接受。
  • 好的,我明白你的意思,但我试图封装这种行为,因为我的代码中有很多地方,我循环 vec 并且不想要相同到处都是代码。我仍然认为下一次迭代会更快,因为向量通过删除条目变得更小,因此对有效性的检查将比以前更少。无论如何,我相信 Luc 的答案对我有用,并且它还将修改与迭代分开,因此迭代器可以真正被视为逻辑容器的迭代器,它不会按照您的建议更改其容器。再次感谢您的帮助。
  • 希望不会过于频繁地删除项目。也许使用 remove-erase 习惯用法偶尔进行一次压缩并跳过正常循环中的无效项目会是一个更好的主意?
【解决方案2】:

关注点分离:容器负责其不变量,迭代器负责遍历。如果将修复移到容器中,则可以将逻辑 constmutable 分开,隐藏部分。

您能否以“最愚蠢”的方式编写迭代器以将它们从容器中分离出来?例如存储一个数字索引(如果它对您的容器有意义),然后调用容器的私人朋友(或更多)来访问 logical 第 n 个元素。

然后可以在const 上重载私人朋友,并且仍然可以修改mutable 部分以进行您描述的修复,然后返回元素。


容器支持随机访问(以及访问的数字索引)的(删节)示例:

template<typename T>
class vector {
    mutable std::vector<std::weak_ptr<T>> data; // notice mutable

    T&
    fetch(int n);

    T const&
    fetch(int n) const; // notice const overload

public:
    class const_iterator;
    friend class const_iterator;

    const_iterator
    begin() const;
};

template<typename T>
class vector<T>::const_iterator {
    int index;
    vector<T> const* this_; // notice const
public:
    // constructors go here

    const_iterator&
    operator++()
    { ++index; }
    // ...

    T const&
    operator*() const
    { return this_->fetch(index); } // this will call the const version of fetch
};

// example implementation of the const version of fetch
template<typename T>
T const&
vector<T>::fetch(int n) const
{
    auto removed = std::remove_if(data.begin(), data.end(), [](std::weak_ptr<T>& element)
    { return element.expired(); });
    // mutate mutable data member in a logically const member
    data.erase(data.begin(), removed);

    // this assumes that there is no race condition
    // bear with me for the sake of understanding the mutable keyword
    return *data[n].lock();
}

【讨论】:

  • 谢谢,这听起来像我的情况。虽然我没看懂你的最后一句话。 “在const 上重载”是什么意思?如果我定义一个const_iterator begin() const {...} 并将迭代传递给this 的引用,它不是必须是const 引用吗?
  • @Flogo 确实如此。当迭代器是然后例如使用operator* 取消引用,它将遵循此引用(让我们将其命名为this_ 并使其成为指针)以调用适当的friend 成员。假设它是return this_-&gt;fetch(current_index);。也应该在 const 上重载这个特定的 fetch 成员。
  • 所以只是为了确保我做对了:然后容器将包含Item fetch(int i);Item fetch(int i) const;?如果是这样,const 版本将如何工作?只是跳过无效条目而不压缩向量?
  • @Flogo mutable 关键字可以帮助将逻辑const 部分与维护类不变量所需的任何机制分开,即使面对const。我的例子有帮助吗?
  • 感谢您的示例,我不知道mutable 关键字。
猜你喜欢
  • 2021-10-30
  • 2010-12-09
  • 2018-03-02
  • 2019-06-20
  • 2014-10-07
  • 2018-04-08
  • 2018-02-13
  • 2011-03-14
  • 2023-03-17
相关资源
最近更新 更多