C++ 라이브러리 / range

c++엔 ranged for이란 문법이 있습니다. 이 문법을 사용하면 반복자를 구현하고 있는 모든 클래스의 원소를 편리하게 순회할 수 있습니다. 예를들어

C++
vector<int> v = { 1, 2, 3, 4, 5 };

for (auto iter = v.begin(); iter != v.end(); ++iter)
  cout << *iter << " ";

위 코드를 아래처럼 쓸 수 있습니다.

C++
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 문법을 통해 비슷한 구조를 만들 수 있습니다.

range.hpp
#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문에서 사용하면 됩니다.

C++
for (auto i : range(1, 23, 3))
  cout << i << " "; // 1, 4, 7, 10, 13, 16, 19, 22 출력 

추천드리진 않습니다만 만약 아래 코드를 헤더에 추가한다면

#define in :

좀 더 파이썬스러움을 연출할 수도 있긴 합니다.

C++
for (auto i in range(7))
  cout << i << " "; // 0, 1, 2, 3, 4, 5, 6 출력 

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다