範囲ベースのforループで使用するC ++ 11の範囲クラスはありますか?


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>()まったく同じに見えます!
Nawaz、2011

2
@Nawaz:ええ、あなたは正しいです。動的なケースと静的なケースを区別して関数ハンドルを作成できるという奇妙なビジョンがありましたが、それができるとは思いません。
全面的

2
@iammilind:Nawazが35分前に同じ質問をしました;)
セバスチャンマッハ

3
わかりやすくするために、この実装にはバグがあると思います。つまり、整数の範囲全体を反復するために使用することはできません。INT_MINとINT_MAXをテンプレート引数としてプラグインする場合、インクリメントされたINT_MAXはオーバーフローしてINT_MINを与え、無限ループを引き起こします。STLの「終了」は、整数型自体に収まらない「終わりを過ぎた」ものと想定されているため、特定のプラットフォームで最も広い整数型に実際に効率的に実装できるかどうかはわかりません。小さい整数型の場合は、内部で常により広い型を使用できます...
Joseph Garvin

回答:


59

C ++標準ライブラリにはありませんが、Boost.Rangeにはboost :: counting_rangeがあります。スコープにもう少し焦点を絞ったboost :: irangeを使用することもできます。

C ++ 20の範囲ライブラリでは、これを使用してこれを行うことができますview::iota(start, end)


3
はい、それは間違いなく私が探しているものの本質です。ブーストがそれをしてくれてうれしいです。標準委員会が何らかの理由でそれを含めなかったのは残念です。これはrange-base-for機能を大幅に補完するものでした。
全面的

この答えの方が私の直接的な質問によく答えるので、Nawazの答えは非常に優れていますが、私はそれを選択します。
全面的

6
範囲を標準(N4128)にする最近の多くの進歩があります。提案とリファレンス実装については、github.com / ericniebler / range-v3を参照してください。
Ela782 2015

1
@ Ela782:...それでも、C ++ 17では表示されないようですよね?
einpoklum 2016年

1
@Andreasはい、範囲は少し前にTSに組み込まれましたが、std::experimental::ranges名前空間の下で主要なコンパイラーに組み込まれた参照実装があるとは考えていません。range-v3常に参照実装の一種でした。しかし、今では基本的な範囲のものも最近C ++ 20に投票されたと思うので、std::まもなくそれをすぐに取り入れます!:-)
Ela782

47

私の知る限り、C ++ 11にはそのようなクラスはありません。

とにかく、私はあなたの実装を改善しようとしました。何も表示されないので、それを非テンプレートにしました優位性をそれ作るのテンプレートを。逆に、1つの大きな欠点があります。コンパイル時にテンプレート引数自体を知る必要があるため、実行時に範囲を作成できないことです。

//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
私はそれが好きです。テンプレート以外のバージョンについて考えました。そして、値が実際に一定である場合、優れたコンパイラーはそれを適切に最適化すると思います。私はそれをテストする必要があります。
全面的

10
@Nawaz:私はまだそれをテンプレート化します、整数型で:)私はまた、へのエイリアスiteratorを提案しconst_iteratoriteratorから派生し、std::iteratorそしてとをrange実装cbegincendます。ああ、なぜconst参照をiterator::operator++返すのですか?
Matthieu M.

6
@RedX:ダイクストラは、範囲のラベル付けがに最適である理由について優れた記事を書いてい[begin, end)ます。@OP:しゃれではない範囲ベースのループのしゃれのための+1 :-)
Kerrek SB '25

2
非テンプレートバージョンの利点は、ループの長さをコンパイル時に知る必要がないことです。もちろん、整数型をテンプレート化することもできます。
CashCow 2013

2
@weeska:そのオーバーロードは、インクリメント操作が行われる前v++値を返すことになっているpostfixインクリメントを実装することになっています。私はあなたが違いを探求することをお勧めしたいとどこであると宣言されます。++ii++iint
Nawaz

13

rangeランタイム範囲であることを除いて、まったく同じ目的で呼び出されるライブラリを作成しましたが、私の場合のアイデアはPythonに由来しています。コンパイル時のバージョンを検討しましたが、私の意見では、コンパイル時のバージョンを利用するメリットはありません。ライブラリはbitbucketにあり、Boost License:Rangeの下にあります。これは、C ++ 03と互換性のある1ヘッダーのライブラリーで、C ++ 11の範囲ベースのforループでチャームのように機能します。

特徴

  • すべてのベルとホイッスルを備えた真のランダムアクセスコンテナー!

  • 範囲は辞書式に比較できます。

  • 数値の存在をチェックする2つの関数exist(boolを返す)、およびfind(イテレータを返す)。

  • ライブラリはCATCHを使用して単体テストされています。

  • 基本的な使用法の例、標準コンテナの操作、標準アルゴリズムの操作、範囲ベースのforループの操作。

こちらが1分の紹介です。最後に、この小さなライブラリについての提案を歓迎します。


1分間の紹介では、私はWikiにアクセスできないと言っています。wikiを公開する必要があります。
Nicol Bolas、2011

@Nicol Bolas申し訳ありませんが、現在公開されています:)
AraK '28

これをありがとう、それは素晴らしいです。もっと多くの人に知ってもらいたいです。
Rafael Kitover、

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))すべてのパラメータ(この場合はを含む)を括弧で囲むという非悪のマクロの基本ルールの1つに従わなかったため、が驚くべき結果をもたらすことに注意してくださいb。また、このアプローチでは、マクロではない「範囲オブジェクト」ベースのアプローチ(Nawazの回答など)よりもパフォーマンス上の利点はありません。
Quuxplusone 2014

2

これは私にとってうまく機能しているより単純なフォームです。私のアプローチにはリスクがありますか?

r_iteratorは、のように可能な限り動作するタイプlong intです。したがって、==およびなどの多くの演算子は++、単にに渡されますlong intoperator long intおよびoperator 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の代わりにstatic のメソッドを作成できます。)


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では、printIntをOFCをラムダで置き換えることができることに注意してください。また、この使用法のもう1つの小さなバリエーションは(厳密にはrandom_iteratorの場合)

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

Fwdのみのイテレータ

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

これをどのように使用しますか?関数にラムダを使用すると思いますが、よくわかりません。
全面的

1
そうだと思いますが、それを使う正しい方法だと思ったら答えを受け入れるでしょう。:P冗談です。例はすでに投稿されています。
Ajeet Ganga 2011

ここではラムダを使用できるので、auto range = myMultiMap.equal_range(key); for_each(range.first、range.second、[&](decltype(* range.first)const&item){//ここにコードを挿入});
CashCow 2017年

-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クラスは範囲をモデル化するものとします。しかし、あなたは文字通りそれを構築しています。それはメモリとメモリアクセスの無駄です。要素の数と最初の要素の値(存在する場合)を除いて、ベクトルは実際の情報を保持しないため、解は非常に冗長です。
ユーザーではない

はい、これは非常に非効率的です。
2017年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.