Есть ли класс диапазона в C++11 для использования с диапазоном, основанным на циклах?
Я обнаружил, что пишу это совсем недавно:
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
}
Теперь я знаю, что я написал не лучший код. И, возможно, есть способ сделать его более гибким и полезным. Но мне кажется, что-то вроде этого должно было стать частью стандарта.
Так что же? Была ли добавлена какая-то новая библиотека для итераторов в диапазоне целых чисел, или, может быть, общий диапазон вычисленных скалярных значений?
8 ответов
В стандартной библиотеке C++ такой библиотеки нет, но Boost.Range имеет boost:: counting_range, что, безусловно, подходит. Вы также можете использовать boost:: irange, который немного более сфокусирован.
Насколько я знаю, в 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
Я написал библиотеку под названием range
для той же цели, за исключением того, что это диапазон времени выполнения, и идея в моем случае пришла от Python. Я рассмотрел версию во время компиляции, но, по моему скромному мнению, нет никакой реальной выгоды получить версию во время компиляции. Вы можете найти библиотеку в bitbucket, и она находится под Boost License: Range. Это библиотека с одним заголовком, совместимая с C++03 и работающая как цикл с циклами для C++11:)
Особенности:
Настоящий контейнер с произвольным доступом со всеми прибамбасами!
Диапазоны можно сравнить лексикографически.
Две функции
exist
(возвращает bool) иfind
(возвращает итератор), чтобы проверить существование числа.Библиотека тестируется с использованием CATCH.
Примеры базового использования, работа со стандартными контейнерами, работа со стандартными алгоритмами и работа с диапазоном на основе циклов.
Вот минутное вступление. Наконец, я приветствую любые предложения об этой крошечной библиотеке.
Я нашел это boost::irange
был намного медленнее, чем каноническая целочисленная петля. Поэтому я остановился на следующем, гораздо более простом решении с использованием макроса препроцессора:
#define RANGE(a, b) unsigned a=0; a<b; a++
Тогда вы можете выполнить цикл следующим образом:
for(RANGE(i, n)) {
// code here
}
Этот диапазон автоматически начинается с нуля. Это может быть легко расширено, чтобы начать с данного числа.
Вот более простая форма, которая хорошо работает для меня. Есть ли риски в моем подходе?
r_iterator
это тип, который ведет себя, насколько это возможно, как long int
, Поэтому многие операторы, такие как ==
а также ++
Просто пройдите к long int
, Я "выставляю" базовый длинный int через operator long int
а также operator 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
статические вместо постоянных.)
Это может быть немного поздно, но я только что увидел этот вопрос, и я уже некоторое время использую этот класс:
#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;
}
Вы пытались использовать
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 можно заменить OFC на лямбду в C++0x. Также может быть еще один маленький вариант этого использования (строго для random_iterator)
for_each(v.begin()+5,v.begin()+10,printInt);
Для Fwd только итератор
for_each(advance(v.begin(),5),advance(v.begin(),10),printInt);
Вы можете легко сгенерировать возрастающую последовательность в C++11, используя std::iota():
#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;
}