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


101

我发现自己是在不久前写的:

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>();
}

这使我可以编写如下内容:

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

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

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


17
+1。我想在我的实用程序中有这样的类。:-)
Nawaz

2
顺便说一下,编写range模板功能的目的是什么?它不会对使用的内容添加任何内容range_class。我的意思是,range<0,10>()range_class<0,10>()看起来完全相同!
纳瓦兹

2
@Nawaz:是的,你是对的。我有一个奇怪的想法,我可以使函数处理动态情况和静态情况不同,但是我认为这是不可能的。
2011年

2
@iammilind:Nawaz在您前面35分钟问了同样的问题;)
Sebastian Mach

3
出于学究,我认为此实现存在一个错误,即您不能使用它来遍历整个整数范围。如果插入INT_MIN和INT_MAX作为模板参数,则INT_MAX递增时将溢出,从而产生INT_MIN并导致无限循环。STL中的“ end”应该是“ end end of end”,它不能放入整数类型本身,因此我不知道对于给定平台上最宽的整数类型实际上可以有效地实现这一点。对于较小的整数类型,您始终可以在内部使用更广泛的类型...
Joseph Garvin 2012年

Answers:


59

C ++标准库没有一个,但是Boost.Range具有boost :: counting_range,它当然可以满足要求。您还可以使用boost :: irange,它在范围上更加集中。

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


3
是的,那绝对是我想要的东西的本质。我很高兴Boost做到了。我很遗憾标准委员会出于任何原因都没有将其包括在内。这将是对基于范围的功能的巨大补充。
2011年

这个答案更好地回答了我的直接问题,因此即使Nawaz的回答非常好,我也会选择它。
2011年

6
最近,将范围纳入标准(N4128)已经取得了很大进展。有关建议和参考实现,请参见github.com/ericniebler/range-v3
Ela782

1
@ Ela782:...但是似乎我们在C ++ 17中看不到它,对吧?
einpoklum '16

1
@Andreas是的,range不久前就将其放入TS,但我认为没有/曾经有参考实现将其放入std::experimental::ranges命名空间下的主要编译器中。range-v3一直都是我要说的参考实现。但是现在我相信基本范围方面的东西最近也被选入了C ++ 20,因此我们确实会std::很快得到它!:-)
Ela782

47

据我所知,C ++ 11中没有此类。

无论如何,我试图改善您的实施。我将其设置为非模板,因为我认为将其设置为模板没有任何优势。相反,它有一个主要缺点:无法在运行时创建范围,因为您需要在编译时本身就知道模板参数。

//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!

这是代码:

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_;
};

测试代码:

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";
}

输出:

10 11 12 13 14 15 16 17 18 19

Onine演示


3
我喜欢。我想到了一个非模板版本。而且我认为如果值实际上是恒定的,那么好的编译器会对其进行优化。我必须测试一下。
2011年

10
@Nawaz:我仍将其作为整数类型的模板:)我也建议使用别名 iteratorconst_iterator,有iterator从派生std::iterator并具有range实现cbegincend。哦,...为什么iterator::operator++返回const引用?
Matthieu M.

6
@RedX:Dijkstra很好地说明了为什么范围标记最好为[begin, end)。@OP:不是基于双关语的基于范围的循环中双关语+1 :-)
Kerrek SB 2011年

2
非模板版本的优点在于,在编译时不需要知道循环的长度。当然,您可以使整数类型成为模板。
CashCow 2013年

2
@weeska:该重载应该实现后缀增量v++,该增量应该在进行增量操作之前返回该值。我建议您探索一下++ii++在哪里之间的区别i被宣布为int
Nawaz 2015年

13

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

特点

  • 一个真正的随机存取容器,带有所有的铃铛和口哨声!

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

  • 有两个函数exist(返回布尔值)和find(返回迭代器)以检查数字的存在。

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

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

这是一分钟的介绍。最后,我欢迎任何有关此小型库的建议。


一分钟的介绍说我无权访问Wiki。您需要公开您的Wiki。
Nicol Bolas

@Nicol Bolas,我真的很抱歉,现在公开了:)
AraK 2011年

谢谢你,这太神奇了。我觉得更多的人应该知道这一点。
拉斐尔·基托弗

5

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

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

然后,您可以像这样循环:

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

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


7
请注意,这for (RANGE(i, flag? n1: n2))将产生令人惊讶的结果,因为您没有遵循非邪恶宏的基本规则之一,即要括住所有参数(在本例中包括b)。与基于非宏“范围对象”的方法(例如Nawaz的答案)相比,您的方法也没有提供任何性能优势。
Quuxplusone 2014年

2

这是一个简单的表格,对我来说很好用。我的方法有风险吗?

r_iterator是尽可能表现得像的类型long int。因此,许多运算符(例如==++)只是传递给long int。我通过operator long intoperator long int &转换“公开”了底层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;
}

编辑: -我们可以使range静态方法代替const。)


1

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

#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>;

用法:

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;
}

0

你尝试过使用

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

大多数时候都符合要求。

例如

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);

}

请注意,在C ++ 0x中,可以使用lambda替换printInt的OFC。另外,此用法的另一个小变化可能是(仅限于random_iterator)

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

对于仅Fwd迭代器

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

您将如何使用它?我猜想您会为该函数使用lambda,但不确定。
2011年

1
可以告诉您,但是如果您认为正确的使用方式,您将接受答案。:P开玩笑。已经发布了示例。
Ajeet Ganga

您可以在此处使用lambda,因此auto range = myMultiMap.equal_range(key); for_each(range.first,range.second,[&](decltype(* range.first)const&item){//代码在这里});
CashCow

-3

您可以使用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;
}

3
range级应的范围内进行建模。但是,您实际上是在构建它。那是浪费内存和内存访问。该解决方案是高度冗余的,因为矢量除元素数量和第一个元素的值(如果存在)外不保存任何实际信息。
非用户

是的,这效率很低。
全天
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.