2010-12-10 12 views
11

Cóż, wiem dlaczego, ponieważ nie ma konwersji, ale dlaczego nie ma konwersji? Dlaczego można przekazywać iteratory na odwrotne iteratory, ale nie na odwrót? I co ważniejsze, co mogę zrobić, jeśli chcę to zrobić? Czy jest jakiś adapter, który pozwala na iterację wstecz za pomocą iteratora do przodu?Dlaczego nie mogę przekonwertować odwrotnego iteratora na kolejny iterator?

std::vector<int> buffer(10); 
std::vector<int>::iterator forward = buffer.begin(); 
std::vector<int>::reverse_iterator backward = buffer.rbegin(); 
++forward; 
++backward; 
std::vector<int>::iterator forwardFromBackward = std::vector<int>::iterator(backward); // error! Can't convert from reverse_iterator to iterator! 
std::vector<int>::reverse_iterator backwardFromForward = std::vector<int>::reverse_iterator(forward); // this is fine 
+14

Jest konwersja: 'backward.base();' – ybungalobill

+0

Jestem całkiem pewien, że można użyć 'reverse_iterator 'gdzie jest to konieczne, przekazując typ iteratora jako argument szablonu wszędzie tam, gdzie używasz' iteratora'. Czy to nie rozwiązuje twojego problemu? – Grozz

+1

Masz całkowitą rację ybungalobill; mój rzeczywisty kod jest nieco bardziej skomplikowany niż to i próbowałem base(), ale dało mi to błędy, więc zrezygnowałem z tego i mój mózg wymazał moją wiedzę na ten temat. Dzięki! Jeśli dodasz ten dokładny tekst jako odpowiedź, zaakceptuję to, ponieważ tak naprawdę * jest * odpowiedzią. –

Odpowiedz

15

Można napisać funkcję pomocnika. Jedną z cech charakterystycznych reverse_iterator jest to, że base() podaje następny iterator, który jest następny od wartości odwrotnej iteratora. This is because a reverse iterator physically points to the element after the one it logically points to. Aby mieć iterator do przodu do tego samego elementu co Twój reverse_iterator, musisz zmniejszyć wynik o base() przez jeden, lub możesz zwiększyć odwrotny iterator najpierw, a następnie podjąć .base() z tego.

Oba przykłady są przedstawione poniżej:

#include <iostream> 
#include <vector> 
#include <iterator> 

//result is undefined if passed container.rend() 
template <class ReverseIterator> 
typename ReverseIterator::iterator_type make_forward(ReverseIterator rit) 
{ 
    return --(rit.base()); // move result of .base() back by one. 
    // alternatively 
    // return (++rit).base() ; 
    // or 
    // return (rit+1).base(). 
} 

int main() 
{ 
    std::vector<int> vec(1, 1); 
    std::vector<int>::reverse_iterator rit = vec.rbegin(); 
    std::vector<int>::iterator fit = make_forward(rit); 
    std::cout << *fit << ' ' << *rit << '\n'; 
} 

Uwaga: Zachowanie to różni się od tego z konstruktora reverse_iterator(iterator).

+0

Prawdopodobnie make_forward() powinien być wyspecjalizowany dla wskaźników. – Abyx

+0

Wygląda na to, że ybungalobill nie chce powtórzyć, więc jako pierwszą odpowiedź wspomnę o base(), którą wygrasz :) Dzięki! –

+0

@Abyx: Tak, ponieważ nie można zmniejszyć wskaźnika tymczasowego zwrócony z 'rit.base()', może lepiej najpierw utworzyć nazwaną zmienną o typie 'iterator_type'. Jednak żadna specjalizacja nie powinna być potrzebna. – visitor

0

można uzyskać z przodu iterator odwrotny iterator przy użyciu tego kodu

container.begin() + (reverseIter - container.rbegin() - 1); 
+0

_Why_. Na dużej liście prosi się o nadmierne przemierzanie, a to jest znacznie bardziej skomplikowane niż po prostu '(reverseIter + 1) .base()' – bobobobo

+0

Jaką listę? to jest * wektor *. – Abyx

+0

Jeśli używasz iteratorów (w przeciwieństwie do prostego indeksowania całkowitoliczbowego), zakładam, że robisz to, aby móc zastosować tę formułę do dowolnego 'std :: container', który używa iteratorów. 'std :: list' traversal jest droższe niż' std :: vector', więc jesteś o wiele lepszy od ruchu (tylko 1 element). – bobobobo

1

To bardzo często mają dwa (wsteczny) iteratory obejmują zakres wartości (takich jak w begin(),end() i rbegin(),rend()). Dla dowolnego zakresu opisanego przez dwa odwrócone iteratory rA,rB, zakres rB.base(),rA.base() będzie obejmował ten sam zakres w kierunku do przodu.

#include <iostream> 
#include <iterator> 
#include <vector> 

int main() { 
    std::vector<int> vec{10,11,12,13,14,15}; 

    // spans the range from 13 to 10 
    auto rfirst=std::rbegin(vec)+2; 
    auto rlast=std::rend(vec); 

    // Loops forward, prints 10 11 12 13 
    for(auto it = rlast.base(); it != rfirst.base(); ++it){ 
    std::cout << *it << " "; 
    } 
} 

Jeśli koncepcyjnie jesteś zainteresowany tylko w jednym punkcie (jak np wyniku find_if), a następnie użyć make_forward przez @visitor. Nawet w tym przypadku, chodzi o zakres pomaga śledzić ważności odwrotny iterator:

#include <iostream> 
#include <iterator> 
#include <vector> 
#include <algorithm> 

int main() { 
    std::vector<int> vec{10,11,12,13,14,15}; 

    auto rfirst=std::rbegin(vec); 
    auto rlast=std::rend(vec); 

    auto rfound = std::find_if(rfirst,rlast, [](int v){ return v<13; }); 

    if(rfound != rlast){ 
    std::cout << *rfound << " "; // prints 12 
    auto forwardFound = make_forward(rfound) ; 
    std::cout << *forwardFound << " "; // prints 12 
    } 
} 
Powiązane problemy