2013-04-23 7 views
16

W moim projekcie, chcę podzielić strumieniowo do jakiegoś określonego rodzaju wartości, więc zaimplementować funkcję szablonu jakoDlaczego typ wartości/typ_różnicu/wskaźnik/odniesienie do argumentu typ_insert_iterator/front_insert_iterator/insert_iterator są nieważne?

template <typename TElem, typename TOutputIter> 
TOutputIter SplitSpace(std::istream& IS, TOutputIter result) 
{ 
    TElem elem; 
    while (IS >> elem) 
    { 
     *result = elem; 
     ++result; 
    } 
    return result; 
} 

myślę, że to jest niewygodne, ponieważ muszę wyraźnie podane rodzaju TElem podczas wywoływania go . Na przykład, muszę napisać:

std::vector<int> v; 
SplitSpace<int>(std::cin, back_inserter(v)); 
// I want to it to be SplitSpace(std::cin, back_inserter(v)); 

Próbowałem uzyskać typ wartości z (szablonu) iterator i używane std::iterator_traits następująco:

template <typename TOutputIter> 
TOutputIter SplitSpace(std::istream& IS, TOutputIter result) 
{ 
    typename std::iterator_traits<TOutputIter>::value_type elem; 
    while (IS >> elem) 
    { 
     *result = elem; 
     ++result; 
    } 
    return result; 
} 

Jednakże powyższe kody nie działają na back_insert_iterator. Sprawdziłem kody źródłowe back_insert_iterator/front_insert_iterator/insert_iterator w przestrzeni nazw i stwierdziłem, że value_type/difference_type/pointer/reference są wszystkie void.

Chciałbym wiedzieć, dlaczego te typy są wszystkie void, czy istnieje jakiekolwiek uwzględnienie tego? Innym pytaniem jest, czy możliwe jest zaimplementowanie funkcji SplitSpace bez jawnego nadania typowi elementu, gdy go wywołasz? Dzięki.

+1

Są to tylko iteratory typu write, jaki inny typ powinien mieć typedef? – PlasmaHH

+0

Nie "rodzaj". Są to OutputIteratory, tzn. Tylko do zapisu i niepowtarzalne. Nadal +1 za interesujące pytanie – sehe

+0

Zdecydowanie dziwne, wydaje się, że nie ma żadnego powodu, aby sfałszować 'typedef' kiedy mogą być użyte zarówno do wypakowania * jak i wstawienia elementów ... –

Odpowiedz

2

AFAIK, powinieneś być w stanie uzyskać container_type z iteratora, z którego powinieneś być w stanie uzyskać value_type. Prawdopodobnie w pewnym momencie będziesz chciał specjalizować się na pair. To powinno odpowiadać na drugą część, tak jak na część pierwszą; nie jestem pewien ...

+0

Jak uzyskać typ kontenera z iteratora (nie każdy iterator wyjścia ma typ kontenera)? –

+0

@ ChristianRau, zgodnie z tym, najwyraźniej nie powinno być dla wymienionych iteratorów: http://en.cppreference.com/w/cpp/iterator/front_insert_iterator – Nim

+0

.... nie oznacza, że ​​niestandardowy zdefiniowany iterator nazwie ' Bob' będzie obsługiwał tę funkcję ... to jest twoja prerogatywa ... – Nim

3

Jak wspomniano przez Luc w komentarzach, co chcesz zrobić, można łatwo zrobić z biblioteki standardowej:

std::vector<int> v; 
std::copy(std::istream_iterator(std::cin) 
     , std::istream_iterator() 
     , std::back_inserter(v)); 
3

value_type nie ma sensu w przypadku OutputIterators, ponieważ Iterator wyjścia nie daje dostępu do żadnych wartości, a co ważniejsze, może akceptować szeroki zakres typów wartości.

Jedynym wymogiem dla OutputIterator it jest konieczność wspiera ekspresji *it = ogdzie O jest wartość pewnego typu, który znajduje się w zestawie, które wprawdzie są zapisu dla danego rodzaju iteratorem I (§24.2. 1). Oznacza to, że iterator wyjściowy może potencjalnie akceptować szeroki zakres typów: na przykład jego operator* może zwracać obiekt proxy, który przeciąże operator= dla różnych typów; co w takim razie powinno być value_type?

Na przykład, rozważmy następujący iteracyjnej wyjściowy:

struct SwallowOutputIterator : 
    public std::iterator<output_iterator_tag, void, void, void, void> 
{ 
    struct proxy // swallows anything 
    { 
     template <typename T> 
     void operator=(const T &) {} 
    }; 

    proxy operator*() 
    { 
     return proxy(); 
    } 

    // increment operators 
}; 

Nie ma rozsądny wybór dla value_type tutaj.

To samo rozumowanie dotyczy pointer i reference_type. difference_type nie jest zdefiniowany, ponieważ nie można obliczyć odległości między dwoma wyjściowymi iteratorami, ponieważ są one jednoprzebiegowe.

N.B .: Norma wyraźnie stwierdza, że ​​insert_iterator i jej rodzeństwo muszą odziedziczyć po iterator<output_iterator_tag, void, void, void, void>, więc nie jest to osobliwość Twojej implementacji.

3

W przypadku, gdy po prostu szukasz sposobu, aby uniknąć określić param TElem szablonu, można użyć tego podejścia:

template <typename TOutputIter> 
TOutputIter SplitSpace(std::istream& IS, TOutputIter result) 
{ 
    typedef typename TOutputIter::container_type::value_type TElem; 

    TElem elem; 
    while (IS >> elem) 
    { 
     *result = elem; 
     ++result; 
    } 
    return result; 
} 

... oczywiście, w zależności od TOutputIter rodzaje udajesz używać nie powinieneś używać tego podejścia.

+0

dziękuję, że to powinna być akceptowana odpowiedź, myślę. –

Powiązane problemy