범위 기반 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 2011-08-25

2
그런데 range템플릿 함수 작성의 요점은 무엇 입니까? 사용되는 사용법에 아무것도 추가하지 않습니다 range_class. 내 말은, range<0,10>()그리고 range_class<0,10>()정확히 같은 봐!
Nawaz

2
@Nawaz : 그래, 당신이 맞아요. 동적 케이스와 정적 케이스를 구별하는 함수 핸들을 만들 수 있다는 이상한 비전이 있었지만 할 수 있다고 생각하지 않습니다.
Omnifarious 2010 년

2
@iammilind : 나와 즈는 35 분 앞서 같은 질문을했다)
세바스찬 마하에게

3
현명하게 말하자면이 구현에는 전체 정수 범위를 반복하는 데 사용할 수 없다는 버그가 있다고 생각합니다. INT_MIN 및 INT_MAX를 템플릿 인수로 연결하면 증가 할 때 INT_MAX가 오버플로되어 INT_MIN을 제공하고 무한 루프가 발생합니다. STL의 "끝"은 정수 유형 자체에 맞지 않는 "끝을 지나서 하나"여야하므로 주어진 플랫폼에서 가장 넓은 정수 유형에 대해 실제로 효율적으로 구현 될 수 있는지 모르겠습니다. 작은 정수 유형의 당신은 항상 ... 내부적으로 넓은 유형을 사용할 수 있습니다
조셉 가빈

답변:


59

C ++ 표준 라이브러리에는 하나가 없지만 Boost.Range에는 확실히 자격이되는 boost :: counting_range가 있습니다. boost :: irange를 사용할 수도 있습니다.좀 더 범위에 초점을 맞춘 .

C ++ 20의 범위 라이브러리를 사용하면 view::iota(start, end).


3
예, 그것이 제가 찾고있는 것의 본질입니다. Boost가 해줘서 다행입니다. 나는 표준위원회가 어떤 이유로 든 그것을 포함하지 않은 것이 슬프다. 범위 기반 기능을 크게 보완했을 것입니다.
갖가지 잡다한

이 대답은 내 직접적인 질문에 더 잘 대답하므로 Nawaz의 대답이 매우 좋더라도 선택하겠습니다.
Omnifarious

6
최근 범위를 표준 (N4128)으로 가져 오는 데 많은 진전이있었습니다. 제안 및 참조 구현 은 github.com/ericniebler/range-v3 를 참조하십시오 .
Ela782

1
@ Ela782 : ...하지만 우리는 C ++ 17에서 그것을 볼 수 없을 것 같습니다.
einpoklum

1
@Andreas 예, 범위는 얼마 전에 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 1314 15 16 17 18 19

Onine 데모 .


3
나는 그것을 좋아한다. 템플릿이 아닌 버전을 생각했습니다. 그리고 값이 실제로 일정한 경우 좋은 컴파일러가이를 잘 최적화 할 것이라고 생각합니다. 나는 그것을 테스트해야 할 것입니다.
Omnifarious 2011 년

10
@Nawaz : 나는 아직도 내가 또한 별칭에 제안 것 : 일체형에, 그것을 템플릿 줄 iteratorconst_iterator가지고 iterator에서 파생 std::iterator하고있다 range구현 cbegin하고 cend. 아 그리고 ... 왜 const 참조를 iterator::operator++반환 합니까?
Matthieu M.

6
@RedX : Dijkstra 는 왜 범위 라벨링이 [begin, end). @OP : 말장난이 아닌 범위 기반 루프의 말장난 :-)
Kerrek SB

2
비 템플릿 버전의 장점은 컴파일 타임에 루프의 길이를 알 필요가 없다는 것입니다. 물론 정수 유형을 템플릿으로 만들 수 있습니다.
CashCow

2
@weeska : 그 오버로드는 증분 작업이 발생 하기 전에v++ 값을 반환해야하는 후위 증분을 구현 해야합니다 . 난 당신의 차이를 탐구 좋을 걸 과 위치를 선언한다 . ++ii++iint
Nawaz

13

range런타임 범위라는 점을 제외하고는 정확히 동일한 목적으로 호출되는 라이브러리를 작성 했으며 제 경우 아이디어는 Python에서 나왔습니다. 나는 컴파일 타임 버전을 고려했지만, 겸손한 견해로는 컴파일 타임 버전을 얻는 것이 실질적인 이점이 없다고 생각합니다. bitbucket에서 라이브러리를 찾을 수 있으며 Boost License : Range 아래에 있습니다. 있습니다. C ++ 03과 호환되는 단일 헤더 라이브러리이며 C ++ 11의 범위 기반 for 루프에서 매력처럼 작동합니다. :)

특징 :

  • 모든 종소리와 휘파람이있는 진정한 랜덤 액세스 컨테이너!

  • 범위는 사전 식으로 비교할 수 있습니다.

  • 두 가지 함수 exist(부울 반환) 및find (반복자 반환) 숫자의 존재를 확인합니다.

  • 라이브러리는 CATCH를 사용하여 단위 테스트됩니다. .

  • 기본 사용법의 예, 표준 컨테이너 작업, 표준 알고리즘 작업 및 for 루프 기반 범위 작업.

다음은 1 분 소개 입니다. 마지막으로이 작은 라이브러리에 대한 제안을 환영합니다.


1 분 소개는 Wiki에 액세스 할 수 없다고 말합니다. 위키를 공개해야합니다.
Nicol Bolas 2011-08-28

@Nicol Bolas 정말 죄송합니다, 지금 공개되었습니다 :)
AraK

감사합니다. 놀랍습니다. 더 많은 사람들이 그것에 대해 알아야한다고 생각합니다.
Rafael Kitover 19.11.

5

나는 그것이 boost::irange표준 정수 루프보다 훨씬 느리다는 것을 발견했습니다 . 그래서 전 처리기 매크로를 사용하여 다음과 같은 훨씬 더 간단한 솔루션을 결정했습니다.

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

그런 다음 다음과 같이 반복 할 수 있습니다.

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

이 범위는 자동으로 0부터 시작됩니다. 주어진 번호에서 시작하도록 쉽게 확장 할 수 있습니다.


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

( 편집 : -우리는 rangeconst 대신 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);

}

printInt는 C ++ 0x에서 람다로 OFC를 대체 할 수 있습니다. 또한이 사용법의 한 가지 더 작은 변형은 (엄격히 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 Kidding. 이미 예제를 게시했습니다.
Ajeet Ganga

여기서 람다를 사용할 수 있으므로 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클래스는 범위를 모델링된다. 그러나 당신은 말 그대로 그것을 구성하고 있습니다. 이는 메모리 및 메모리 액세스 낭비입니다. 벡터는 요소의 수와 첫 번째 요소의 값 (존재하는 경우)을 제외하고는 실제 정보를 보유하지 않기 때문에 솔루션은 매우 중복됩니다.
하지 - 한 - 사용자

예, 이것은 매우 비효율적입니다.
Omnifarious 2010 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.