C++11 中是否有范围类用于基于范围的 for 循环?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7185437/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 16:34:26  来源:igfitidea点击:

Is there a range class in C++11 for use with range based for loops?

c++c++11std

提问by Omnifarious

I found myself writing this just a bit ago:

我发现自己刚刚在写这个:

template <long int T_begin, long int T_end>
class range_class {
 public:
   class iterator {
      friend class range_class;
    public:
      long int operator *() const { return i_; }
      const iterator &operator ++() { ++i_; return *this; }
      iterator operator ++(int) { iterator copy(*this); ++i_; return copy; }

      bool operator ==(const iterator &other) const { return i_ == other.i_; }
      bool operator !=(const iterator &other) const { return i_ != other.i_; }

    protected:
      iterator(long int start) : i_ (start) { }

    private:
      unsigned long i_;
   };

   iterator begin() const { return iterator(T_begin); }
   iterator end() const { return iterator(T_end); }
};

template <long int T_begin, long int T_end>
const range_class<T_begin, T_end>
range()
{
   return range_class<T_begin, T_end>();
}

And this allows me to write things like this:

这让我可以写这样的东西:

for (auto i: range<0, 10>()) {
    // stuff with i
}

Now, I know what I wrote is maybe not the best code. And maybe there's a way to make it more flexible and useful. But it seems to me like something like this should've been made part of the standard.

现在,我知道我写的可能不是最好的代码。也许有一种方法可以使它更灵活和有用。但在我看来,这样的事情应该成为标准的一部分。

So is it? Was some sort of new library added for iterators over a range of integers, or maybe a generic range of computed scalar values?

是吗?是否为整数范围内的迭代器添加了某种新库,或者可能是计算标量值的通用范围?

采纳答案by Nicol Bolas

The C++ standard library does not have one, but Boost.Range has boost::counting_range, which certainly qualifies. You could also use boost::irange, which is a bit more focused in scope.

C++ 标准库没有,但Boost.Range 有 boost::counting_range,这当然有资格。您还可以使用boost::irange,它的范围更集中一些。

C++20's range library will allow you to do this via view::iota(start, end).

C++20 的范围库将允许您通过view::iota(start, end).

回答by Nawaz

As far as I know, there is no such class in C++11.

据我所知,C++11 中没有这样的类。

Anyway, I tried to improve your implementation. I made it non-template, as I don't see any advantagein making it template. On the contrary, it has one major disadvantage : that you cannot create the range at runtime, as you need to know the template arguments at compile time itself.

无论如何,我试图改进您的实施。我做了非模板,因为我看不到任何优势在使其模板。相反,它有一个主要缺点:您无法在运行时创建范围,因为您需要在编译时知道模板参数本身。

//your version
auto x = range<m,n>(); //m and n must be known at compile time

//my version
auto x = range(m,n);  //m and n may be known at runtime as well!

Here is the code:

这是代码:

class range {
 public:
   class iterator {
      friend class range;
    public:
      long int operator *() const { return i_; }
      const iterator &operator ++() { ++i_; return *this; }
      iterator operator ++(int) { iterator copy(*this); ++i_; return copy; }

      bool operator ==(const iterator &other) const { return i_ == other.i_; }
      bool operator !=(const iterator &other) const { return i_ != other.i_; }

    protected:
      iterator(long int start) : i_ (start) { }

    private:
      unsigned long i_;
   };

   iterator begin() const { return begin_; }
   iterator end() const { return end_; }
   range(long int  begin, long int end) : begin_(begin), end_(end) {}
private:
   iterator begin_;
   iterator end_;
};

Test code:

测试代码:

int main() {
      int m, n;
      std::istringstream in("10 20");
      if ( in >> m >> n ) //using in, because std::cin cannot be used at coliru.
      {
        if ( m > n ) std::swap(m,n); 
        for (auto i : range(m,n)) 
        {
             std::cout << i << " ";
        }
      }
      else 
        std::cout <<"invalid input";
}

Output:

输出:

10 11 12 13 14 15 16 17 18 19

10 11 12 13 14 15 16 17 18 19

Onine demo.

在线演示

回答by AraK

I wrote a library called rangefor exactly the same purpose except it is a run-time range, and the idea in my case came from Python. I considered a compile-time version, but in my humble opinion there is no real advantage to gain out the compile-time version. You can find the library on bitbucket, and it is under Boost License: Range. It is a one-header library, compatible with C++03 and works like charm with range-based for loops in C++11 :)

我写了一个range完全相同目的的库,只是它是一个运行时范围,在我的例子中这个想法来自 Python。我考虑过编译时版本,但在我看来,获得编译时版本并没有真正的优势。您可以在 bitbucket 上找到该库,它位于 Boost License: Range 下。它是一个单头库,与 C++03 兼容,并且在 C++11 中使用基于范围的 for 循环就像魅力一样工作:)

Features:

特点

  • A true random access container with all the bells and whistles!

  • Ranges can be compared lexicographically.

  • Two functions exist(returns bool), and find(returns iterator) to check the existence of a number.

  • The library is unit-tested using CATCH.

  • Examples of basic usage, working with standard containers, working with standard algorithms and working with range based for loops.

  • 一个真正的随机访问容器,具有所有花里胡哨的功能!

  • 范围可以按字典顺序进行比较。

  • 两个函数exist(返回 bool)和find(返回迭代器)来检查数字是否存在。

  • 该库使用CATCH进行单元测试。

  • 基本用法示例、使用标准容器、使用标准算法以及使用基于范围的 for 循环。

Here is a one-minute introduction. Finally, I welcome any suggestion about this tiny library.

这是一个一分钟的介绍。最后,我欢迎任何关于这个小图书馆的建议。

回答by user2664470

I found that boost::irangewas much slower than the canonical integer loop. So I settled on the following much simpler solution using a preprocessor macro:

我发现这boost::irange比规范整数循环慢得多。因此,我使用预处理器宏确定了以下更简单的解决方案:

#define RANGE(a, b) unsigned a=0; a<b; a++

Then you can loop like this:

然后你可以像这样循环:

for(RANGE(i, n)) {
    // code here
}

This range automatically starts from zero. It could be easily extended to start from a given number.

该范围自动从零开始。它可以很容易地扩展到从给定的数字开始。

回答by Aaron McDaid

Here is a simpler form which is working nicely for me. Are there any risks in my approach?

这是一个更简单的形式,对我来说很好用。我的方法有什么风险吗?

r_iteratoris a type which behaves, as much as possible, like a long int. Therefore many operators such as ==and ++, simply pass through to the long int. I 'expose' the underlying long int via the operator long intand operator long int &conversions.

r_iterator是一种行为尽可能像 a 的类型long int。因此,许多运算符,例如==and ++,只是简单地传递到long int. 我通过operator long intoperator long int &转换“暴露”了底层的长整型。

#include <iostream>
using namespace std;

struct r_iterator {
        long int value;
        r_iterator(long int _v) : value(_v) {}
        operator long int () const { return value; }
        operator long int& ()      { return value; }
        long int operator* () const { return value; }
};
template <long int _begin, long int _end>
struct range {
        static r_iterator begin() {return _begin;}
        static r_iterator end  () {return _end;}
};
int main() {
        for(auto i: range<0,10>()) { cout << i << endl; }
        return 0;
}

(Edit:- we can make the methods of rangestatic instead of const.)

编辑:- 我们可以使方法range静态而不是常量。)

回答by OneOfOne

This might be a little late but I just saw this question and I've been using this class for a while now :

这可能有点晚了,但我刚看到这个问题,我已经使用这个类有一段时间了:

#include <iostream>
#include <utility>
#include <stdexcept>

template<typename T, bool reverse = false> struct Range final {
    struct Iterator final{
        T value;
        Iterator(const T & v) : value(v) {}
        const Iterator & operator++() { reverse ? --value : ++value; return *this; }
        bool operator!=(const Iterator & o) { return o.value != value; }
        T operator*() const { return value; }
    };
    T begin_, end_;
    Range(const T & b, const T & e)  : begin_(b), end_(e) {
        if(b > e) throw std::out_of_range("begin > end");
    }

    Iterator begin() const { return reverse ? end_ -1 : begin_; }
    Iterator end() const { return reverse ? begin_ - 1: end_; }

    Range() = delete;
    Range(const Range &) = delete;
};

using UIntRange = Range<unsigned, false>;
using RUIntRange = Range<unsigned, true>;

Usage :

用法 :

int main() {
    std::cout << "Reverse : ";
    for(auto i : RUIntRange(0, 10)) std::cout << i << ' ';
    std::cout << std::endl << "Normal : ";
    for(auto i : UIntRange(0u, 10u)) std::cout << i << ' ';
    std::cout << std::endl;
}

回答by Ajeet Ganga

have you tried using

你有没有试过使用

template <class InputIterator, class Function>
   Function for_each (InputIterator first, InputIterator last, Function f);

Most of the time fits the bill.

大多数时间都符合要求。

E.g.

例如

template<class T> void printInt(T i) {cout<<i<<endl;}
void test()
{
 int arr[] = {1,5,7};
 vector v(arr,arr+3);

 for_each(v.begin(),v.end(),printInt);

}

Note that printInt can OFC be replaced with a lambda in C++0x. Also one more small variation of this usage could be (strictly for random_iterator)

请注意,在 C++0x 中,printInt 可以被 OFC 替换为 lambda。这种用法的另一个小变化可能是(严格用于 random_iterator)

 for_each(v.begin()+5,v.begin()+10,printInt);

For Fwd only iterator

对于 Fwd only 迭代器

 for_each(advance(v.begin(),5),advance(v.begin(),10),printInt);

回答by blue scorpion

You can easily generate an increasing sequence in C++11 using std::iota():

您可以使用 std::iota() 在 C++11 中轻松生成递增序列:

#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>

template<typename T>
std::vector<T> range(T start, T end)
{
  std::vector<T> r(end+1-start, T(0));
  std::iota(r.begin(), r.end(), T(start));//increasing sequence
  return r;
}

int main(int argc, const char * argv[])
{
  for(auto i:range<int>(-3,5))
    std::cout<<i<<std::endl;

  return 0;
}