基于c++11中的范围

range-based for in c++11

本文关键字:范围 c++11 基于      更新时间:2023-10-16

在c++11中,如果我们有一个set<int> S;我们可以说:

for (auto i: S)
    cout << i << endl;

但是我们可以强制i成为迭代器吗?我的意思是写一个等价于的代码

for (auto i = S.begin(); i != S.end(); i++)
    cout << (i != s.begin()) ? " " : "" << *i;

或者我们可以做一些事情来理解集合(或向量)中i的索引吗?

另一个问题是,我们怎么能说不对S中的所有元素都这样做,而是对它们的前一半或除第一个元素外的所有元素这样做。

或者当我们有一个vector<int> V,并且想要打印它的第一个n值时,我们应该怎么做?我知道我们可以创建一个新的向量,但将一个向量复制到一个新向量需要时间。

不,很不幸。看看标准是怎么说的:

基于语句的范围for(用于范围声明:表达式)语句相当于

{
    auto && __range = ( expression );
    for ( auto __begin = begin-expr, __end = end-expr; __begin != __end; ++__begin ) {
        for-range-declaration = *__begin;
        statement
    }
}

其中__range、__begin和__end是为仅展示定义的变量

换句话说,它已经从begin迭代到end,并且已经取消了对迭代器的引用,这是您永远看不到的。

基于范围的for的原理是在整个范围内迭代。

但是决定范围是什么,因此您可以对范围本身进行操作。

template <typename It>
class RangeView {
public:
  typedef It iterator;
  RangeView(): _begin(), _end() {}
  RangeView(iterator begin, iterator end): _begin(begin), _end(end) {}
  iterator begin() const { return _begin; }
  iterator end() const { return _end; }
private:
  iterator _begin;
  iterator _end;
};
template <typename C>
RangeView<typename C::iterator> rangeView(C& c, size_t begin, size_t end) {
  return RangeView<typename C::iterator>(
           std::next(c.begin(), begin),
           std::next(c.begin(), end)
         );
}
template <typename C>
RangeView<typename C::const_iterator> rangeView(C const& c, size_t begin, size_t end) {
  return RangeView<typename C::const_iterator>(
           std::next(c.begin(), begin),
           std::next(c.begin(), end)
         );
}

好的,这是一个严重的重组Boost.Range…

现在,让我们使用它!

for (auto i: rangeView(set, 1, 10)) {
  // iterate through the second to the ninth element
}

不,你不能。

for (... : ...)

仅由于不引入新的关键字的原因而被称为CCD_ 10而不是CCD_。foreach的全部要点是一个快速的简短语法,用于迭代所有元素而不关心它们的索引。对于所有其他情况,有一个简单的for,它非常有效地达到了它的目的。

对于一般情况,您必须使用一个单独的变量:

int i = 0;
for (auto x : s)
    cout << (i++ ? " " : "") << x << endl;

当然,对于某些容器(如vector)也有一些技巧,但没有一个适用于每个容器。

为此,您最好使用普通的for循环。

您不能在set。使用传统的for语法或维护自己的索引计数器。

您可以vector或其他具有平面布局(如std::array或C样式数组)的容器中。将其更改为使用引用

for (auto &i: S)

然后可以将i的地址与s[0]的地址进行比较以获得索引。

基于范围的for适用于简单的情况。我希望在原型化时能稍微有用一些,但我希望在东西真正成为产品之前很久,它的用途就已经消失了。让初学者的生活更轻松可能很有用,但这是一个我无法判断的领域(但这似乎推动了最近许多C++讨论)。

唯一有点建设性的方法是使用一个适配器,该适配器引用底层范围,并且其begin()end()方法适当地调整迭代器。还要注意,您可能希望将对第一个或最后一个元素的任何特殊处理从处理大量数据的循环中提升出来。当然,这只是另一个检查,然后是正确预测的分支,而不是不检查,分支预测表的污染更少。