c++엔 ranged for이란 문법이 있습니다. 이 문법을 사용하면 반복자를 구현하고 있는 모든 클래스의 원소를 편리하게 순회할 수 있습니다. 예를들어
vector<int> v = { 1, 2, 3, 4, 5 };
for (auto iter = v.begin(); iter != v.end(); ++iter)
cout << *iter << " ";
위 코드를 아래처럼 쓸 수 있습니다.
vector<int> v = { 1, 2, 3, 4, 5 };
for (auto val : v)
cout << v << " ";
ranged for 문법을 사용하기 위해선 for 문에 사용되는 클래스가 begin(), end() 메서드를 가지고 있어야 하고 적절한 반복자를 반환해야 합니다. 또한 반복자는 operator*(), operator++(), operator!=() 연산자를 오버로딩 해야 합니다.
파이썬 문법에선 range함수로 for문을 사용할 수 있습니다. C++도 ranged for 문법을 통해 비슷한 구조를 만들 수 있습니다.
#pragma once
#include <assert.h>
template <class T>
class range_iter {
template <class U>
friend class range_t;
range_iter(const T& step, const T& curr) :
step(step), curr(curr) {}
public:
T operator*() const {
return curr;
}
range_iter& operator++() {
curr += step;
return *this;
}
bool operator!=(const range_iter<T>& rhs) const {
if (0 <= step) return curr < rhs.curr;
else return rhs.curr < curr;
}
private:
T step;
T curr;
};
template <class T>
class range_t {
template <class U>
friend range_t<U> range(const U&, const U&, const U&);
template <class U>
friend range_t<U> range(const U&);
range_t(const T& first, const T& last, const T& step) :
first(first), last(last), step(step) {}
public:
range_iter<T> begin() const {
return { step, first };
}
range_iter<T> end() const {
return { step, last };
}
private:
T first;
T last;
T step;
};
template <class T>
range_t<T> range(const T& first, const T& last, const T& step = static_cast<T>(1)) {
assert((0 <= step && first <= last) || (step < 0 && last <= first));
return { first, last, step };
}
template <class T>
range_t<T> range(const T& last) {
return { static_cast<T>(0), last, static_cast<T>(1) };
}
C++헤더는 클래스 두 개와 함수 2개로 이루어져 있습니다. 먼저 위의 range_iter<T>는 반복자 클래스 입니다. 사용자가 임의로 이 반복자를 만들지 못하도록 생성자를 private로 만들었습니다. 또 ranged for문의 반복자가 가져야 할 최소한의 메소드를 선언했습니다. 그리고 range를 float로 사용했을 때 경우 오차 이슈가 있으므로 operator!=() 메서드에선 같은지 다른지가 아니라 누가 더 크고 작은지를 비교합니다.
range_t<T>는 for문에 사용되는 클래스 입니다. 마찬가지로 사용자가 함부로 만들지 못하도록 생성자를 private으로 만들었고 필요한 메서드인 begin()과 end()만 선언했습니다.
함수 두개는 범위와 간격을 받아 range_t<T>를 리턴합니다. 이 두 함수로 아래 세 조합을 만들 수 있습니다.
- range(stop) – 0에서 시작하고 [0, step) 범위에서 1씩 증가하며 반복
- range(start, stop) – start에서 시작하고 [start, step) 범위에서 1씩 증가하며 반복
- range(start, stop, step) – start에서 시작하고 [start, step) 범위에서 step씩 증가하며 반복
여기서 step은 음수도 가능합니다. range(5, -4, -2)를 예를 들어서 사용한다면 5, 3, 1, -1, -3 이렇게 5번 반복합니다.
사용하는 방법은 아주 간단합니다. range함수를 ranged for문에서 사용하면 됩니다.
for (auto i : range(1, 23, 3))
cout << i << " "; // 1, 4, 7, 10, 13, 16, 19, 22 출력
추천드리진 않습니다만 만약 아래 코드를 헤더에 추가한다면
#define in :
좀 더 파이썬스러움을 연출할 수도 있긴 합니다.
for (auto i in range(7))
cout << i << " "; // 0, 1, 2, 3, 4, 5, 6 출력
최신 댓글