Piszę to chwilę temu:
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>();
}
A to pozwala mi pisać takie rzeczy:
for (auto i: range<0, 10>()) {
// stuff with i
}
Teraz wiem, że to, co napisałem, może nie jest najlepszym kodem. A może istnieje sposób, aby uczynić go bardziej elastycznym i użytecznym. Ale wydaje mi się, że coś takiego powinno być częścią standardu.
Więc to jest? Czy dodano jakąś nową bibliotekę dla iteratorów dla zakresu liczb całkowitych, czy może ogólny zakres obliczonych wartości skalarnych?
range
funkcji szablonu? Nie dodaje niczego do zastosowania, w którymrange_class
jest używany. To znaczyrange<0,10>()
irange_class<0,10>()
wyglądają dokładnie tak samo!Odpowiedzi:
Biblioteka standardowa C ++ nie ma takiej biblioteki, ale Boost.Range ma funkcję boost :: counting_range , co z pewnością kwalifikuje. Możesz także użyć boost :: irange , która jest nieco bardziej ukierunkowana na zakres.
Biblioteka zakresów C ++ 20 pozwoli Ci to zrobić za pośrednictwem
view::iota(start, end)
.źródło
std::experimental::ranges
przestrzeni nazw.range-v3
był zawsze w pewnym sensie implementacją referencyjną, powiedziałbym. Ale teraz uważam, że podstawowe rzeczy z zakresu również zostały ostatnio przegłosowane w C ++ 20, więc rzeczywiściestd::
wkrótce je dostaniemy ! :-)O ile wiem, w C ++ 11 takiej klasy nie ma.
W każdym razie próbowałem ulepszyć Twoją implementację. Zrobiłem to jako szablon , ponieważ nie widzę żadnej korzyści w tworzeniu go jako szablonu . Wręcz przeciwnie, ma jedną poważną wadę: nie można utworzyć zakresu w czasie wykonywania, ponieważ trzeba znać argumenty szablonu w czasie kompilacji.
Oto kod:
Kod testowy:
Wynik:
10 11 12 13 14 15 16 17 18 19
Demo online .
źródło
iterator
doconst_iterator
,iterator
wyprowadziłemstd::iterator
irange
zaimplementowałemcbegin
icend
. Aha i ... dlaczegoiterator::operator++
zwraca stałą referencję?[begin, end)
. @OP: +1 za grę słów w pętlach opartych na zasięgu, które nie są kalamburami :-)v++
który ma zwrócić wartość przed wykonaniem operacji przyrostu. Radzę Ci poznać różnicę między++i
ai++
gdziei
jest uznane zaint
.Napisałem bibliotekę wywołaną
range
dokładnie w tym samym celu, z wyjątkiem tego, że jest to zakres czasu wykonywania, a pomysł w moim przypadku pochodzi z Pythona. Rozważałem wersję kompilowaną na czas kompilacji, ale moim skromnym zdaniem nie ma żadnej realnej korzyści z uzyskania wersji na czas kompilacji. Bibliotekę można znaleźć na stronie bitbucket i jest ona objęta licencją Boost: zakres . Jest to biblioteka z jednym nagłówkiem, kompatybilna z C ++ 03 i działa jak urok z pętlami opartymi na zakresie w C ++ 11 :)Cechy :
Prawdziwy kontener o swobodnym dostępie ze wszystkimi dzwonkami i gwizdkami!
Zakresy można porównać leksykograficznie.
Dwie funkcje
exist
(zwraca wartość bool) ifind
(zwraca iterator) do sprawdzania istnienia liczby.Biblioteka jest testowana jednostkowo przy użyciu funkcji CATCH .
Przykłady podstawowego użycia, praca ze standardowymi kontenerami, praca ze standardowymi algorytmami i praca z zakresem w oparciu o pętle.
Oto jednominutowe wprowadzenie . Na koniec z zadowoleniem przyjmuję wszelkie sugestie dotyczące tej małej biblioteki.
źródło
Okazało się, że jest
boost::irange
to znacznie wolniejsze niż kanoniczna pętla liczb całkowitych. Zdecydowałem się więc na następujące, znacznie prostsze rozwiązanie, używając makra preprocesora:Następnie możesz wykonać pętlę w ten sposób:
Ten zakres automatycznie zaczyna się od zera. Można go łatwo rozszerzyć, zaczynając od podanej liczby.
źródło
for (RANGE(i, flag? n1: n2))
przyniesie to zaskakujące wyniki, ponieważ nie zastosowałeś się do jednej z podstawowych zasad makr niezłych, która polega na umieszczeniu wszystkich parametrów w nawiasach (w tym w tym przypadkub
). Twoje podejście nie zapewnia również żadnych korzyści w zakresie wydajności w porównaniu z podejściem innym niż makro, opartym na „obiekcie zakresu” (np . Odpowiedź Nawaza ).Oto prostsza forma, która mi odpowiada. Czy jest jakieś ryzyko w moim podejściu?
r_iterator
jest typem, który zachowuje się, w miarę możliwości, jak along int
. Dlatego wiele operatorów, takich jak==
i++
, po prostu przechodzi dolong int
. „Ujawniam” bazowy long int poprzez konwersjeoperator long int
ioperator long int &
.( Edycja: - możemy uczynić metody
range
statycznymi zamiast const.)źródło
To może być trochę za późno, ale właśnie zobaczyłem to pytanie i używam tej klasy od jakiegoś czasu:
Stosowanie :
źródło
próbowałeś użyć
W większości przypadków pasuje do rachunku.
Na przykład
Zauważ, że printInt można zastąpić OFC przez lambdę w C ++ 0x. Może być jeszcze jedna mała odmiana tego zastosowania (wyłącznie dla random_iterator)
Tylko dla iteratora Fwd
źródło
Możesz łatwo wygenerować rosnącą sekwencję w C ++ 11 używając std :: iota ():
źródło
range
Klasa powinna modelować zakres. Jednak dosłownie go konstruujesz. To strata pamięci i dostępu do pamięci. Rozwiązanie jest wysoce redundantne, ponieważ wektor nie zawiera żadnych prawdziwych informacji poza liczbą elementów i wartością pierwszego elementu (jeśli istnieje).