Hoe hercherden over een vector?

Ik moet het over een vector strikt in de volgorde waarin de elementen erin werden geduwd. Voor mijn specifieke geval is het beter gebruik van iterators dan de voor-elke lus als volgt:

std::vector<int> vector;
for(int i = 0; i < vector.size(); i++)
   //not good, but works

Mijn vraag is of het aldirieel over de vector via iterator is om te iterator:

std::vector<int> v;
for(typename std::vector<int>::iterator i = v.iterator(); i != v.end(); i++)
   //good, but I'm not strictly sure about the iterating order.

Kan ik iterators veilig gebruiken met mijn vereisten? Is het opgesteld?


Antwoord 1, Autoriteit 100%

Als u toegang hebt tot C++ 11, kunt u bereik op basis van lussen gebruiken

for (auto i : v)

Anders moet u begin()en end()

gebruiken

for (std::vector<int>::iterator i = v.begin(); i != v.end(); ++i)

U kunt ook gebruiken std::beginEN std::end(deze vereisen ook C++ 11)

for (std::vector<int>::iterator i = std::begin(v); i != std::end(v); ++i)

beginZal een iterator terugbrengen naar het eerste element in uw vector . endzal een iterator terugbrengen naar één element langs het einde van uw vector . Dus de volgorde waarin u de elementen op deze manier krijgt, is zowel veilig als gedefinieerd.


Antwoord 2, Autoriteit 22%

Als u iterators wilt gebruiken, is uw aanpak prima

for(auto it = v.begin(); it != v.end(); ++it)

De iteratieorder hangt in feite af van de container en de werkelijke gebruiker iterator.
Voor een STD :: Vector deze code herhaalt altijd over de elementen in hun volgorde in de vector. Als u V.RBEGIN () of V.Rend () in plaats daarvan zou gebruiken, zou de bestelling worden omgekeerd.
Voor een andere container is de bestelling anders, voor een STD :: instellen, dezelfde code met de elementen in oplopende volgorde van hun sorteercriterium.


Antwoord 3, Autoriteit 11%

Dit is een overdreven complexe oplossing.

Ten eerste beginnen we met een indexType. Het kan alles opslaan dat iterator en / of integraal (in principe, ondersteunt + scalaire, delta tot scalaire, kopie, vernietiging en gelijkheid):

template<class T,
  class Category=std::random_access_iterator_tag,
  class Delta=decltype(std::declval<T>()-std::declval<T>())
>
struct index:
  std::iterator<Category, T, Delta, T*, T>
{
  T t;
  T operator*()const{ return t; }
  index& operator++(){++t; return *this;}
  index operator++(int){index tmp = *this; ++t; return tmp;}
  index& operator--(){ --t; return *this;}
  index operator--(int){index tmp = *this; --t; return tmp;}
  T operator[](size_t i)const{return t+i;}
  friend bool operator<(index const& lhs, index const& rhs) {
    return rhs.t-lhs.t>0;
  }
  friend bool operator>(index const& lhs, index const& rhs) {
    return rhs<lhs;
  }
  friend bool operator<=(index const& lhs, index const& rhs) {
    return !(lhs>rhs);
  }
  friend bool operator>=(index const& lhs, index const& rhs) {
    return !(lhs<rhs);
  }
  friend bool operator==(index const& lhs, index const& rhs) {
    return lhs.t==rhs.t;
  }
  friend bool operator!=(index const& lhs, index const& rhs) {
    return lhs.t!=rhs.t;
  }
  friend Delta operator-(index const& lhs, index const& rhs) {
    return lhs.t-rhs.t;
  }
  friend index& operator+=(index& lhs, Delta rhs) {
    lhs.t+=rhs;
    return lhs;
  }
  friend index& operator-=(index& lhs, Delta rhs) {
    lhs.t-=rhs;
    return lhs;
  }
  friend index operator+(index idx, Delta scalar) {
    idx+=scalar;
    return idx;
  }
  friend index operator+(Delta scalar, index idx) {
    idx+=scalar;
    return idx;
  }
  friend index operator-(index idx, Delta scalar) {
    idx-=scalar;
    return idx;
  }
};

waarvan de meeste niet nodig zijn, maar ik wilde volledig zijn. Merk op dat dit standaard een random access iterator is, maar het liegt, aangezien het referentietype geen referentie is. Ik vind de leugen de moeite waard.

Met indexkunnen we zowel een iterator-over-integraal als een iterator-over-iterators maken. Hier is hoe we een iterator-over-iterators maken:

using it_category=typename std::iterator_traits<It>::iterator_category;
template<class It, class Category=it_category<It>>
index<It, Category> meta_iterator( It it ) { return {it}; }

Vervolgens willen we enkele iterators kunnen nemen en het bereik kunnen herhalen. Dit betekent dat we een bereiktype willen:

template<class It>
struct range {
  It b, e;
  range(It s, It f):b(s),e(f){}
  range():b(),e(){}
  It begin()const{return b;}
  It end()const{return e;}
  bool empty()const{return begin()==end();}
  template<class R>
  range(R&& r):range(std::begin(r),std::end(r)){}
};

Dit is een eigenschap die een itereerbaar bereik nodig heeft (niet alleen een range, maar ook een willekeurige container) en het iteratortype eruit haalt. Merk op dat een superieure ADL-compatibele versie een goed idee zou zijn:

template<class R>
using iterator=decltype(std::begin(std::declval<R&>()));

Willekeurige helpers:

template<class R,class It=iterator<R>>
range<It> make_range(R&&r){ return {std::forward<R>(r)}; }
template<class It
range<It> make_range(It s, It f){return {s,f};}

Hier is een handige hulp die ons probleem oplost:

template<class R>
range<meta_iterator<iterator<R>> iterators_into( R&& r ){
  return {meta_iterator(std::begin(r)), meta_iterator(std::end(r))};
}

en we zijn klaar:

std::vector<int> v;
for(auto it: iterators_into(v)) {
}

retourneert een iterator voor elk element van v.

Voor industriële kwaliteit zou je dingen willen verbeteren met ADL. Door de rvalue-opslag van invoerbereiken af te handelen, kunt u bereikadapters ook beter koppelen in for(:)-lussen.

Other episodes