2012-01-05 12 views
6

Mam funkcję, która musi wielokrotnie wyliczyć iterator, ale according to MSDN, "Po zwiększeniu dowolnej kopii iteratora wejściowego, żadna inna kopia nie może być bezpiecznie porównywana, dereferencyjna lub zwiększana po tym czasie."Jak ograniczyć iterator do bycia przyszłym iteratorem?

Aby to ułatwić, zamiast tworzyć oddzielną implementację dla nie-forward-iteratorów, które kopiują dane i wyliczają kopię, chcę ograniczyć moją metodę tylko do przyjmowania kolejnych iteratorów, i odrzucając statycznie iteratory wejściowe .

Teraz mam coś takiego:

template<typename It, typename TCallback /*signature: bool(value_type)*/> 
bool EnumerateTwice(const It &begin, const It &end, TCallback callback) 
{ 
    for (It it = begin; it != end; ++it) 
     if (!callback(*it)) 
      return false; 
    for (It it = begin; it != end; ++it) 
     if (!callback(*it)) 
      return false; 
    return true; 
} 

ale nic nie ogranicza It do bycia naprzód iterator.

Jak umieścić to ograniczenie na funkcji szablonowej? (C++ 03)

Odpowiedz

5

Można użyć SFINAE i zastąpić bool przez:

typename enable_if< 
    is_same<typename std::iterator_traits<It>::iterator_category, 
      std::forward_iterator_tag>::value, 
    bool>::type 

może trzeba określić is_same i enable_if siebie, jeśli nie chcesz, aby wyciągnąć je z podwyższenia lub TR1:

template <typename A, typename B> 
struct is_same { static const bool value = false; }; 

template <typename T> 
struct is_same<T, T> { static const bool value = true; }; 

template <bool, typename> struct enable_if { }; 
template <typename T> struct enable_if<true, T> { typedef T type; }; 
+0

+1 Oooooo nie wiedział, że iteratory są oznaczone tagami! To bardzo przydatne. Wielkie dzięki! :) – Mehrdad

+9

Spowoduje to odrzucenie iteratorów dwukierunkowych i losowych. Użyj 'is_base_of' zamiast' is_same' dla właściwej semantyki. – ildjarn

+0

@ildjarn: O tak, dobry punkt; dzięki! – Mehrdad

4

nie testowano, ale można spróbować czegoś wzdłuż linii:

template<typename It, typename TCallback /*signature: bool(value_type)*/> 
bool EnumerateTwice_Interal(const It &begin, const It &end, TCallback callback, 
     std::forward_iterator_tag) 
{ 
    //do your things 
} 

template<typename It, typename TCallback /*signature: bool(value_type)*/> 
bool EnumerateTwice(const It &begin, const It &end, TCallback callback) 
{ 
    EnumerateTwice_Internal(begin, end, callback, 
     typename std::iterator_traits<It>::iterator_category()); 
} 
+1

+1 za unikanie SFINAE - to jest czystsze IMO. – ildjarn

3

Można to zrobić za pomocą std::enable_if:

#include <iterator> 
#include <type_traits> 
#include <utility> 

template <typename It, typename TCallback> 
typename std::enable_if<std::is_base_of<std::forward_iterator_tag, 
         typename std::iterator_traits<It>::iterator_category>::value, 
        bool>::type 
EnumerateTwice(It begin, It end, TCallback) { 
    ... 
} 

ta wykorzystuje klasę od C++ 11, ale to wszystko można zrobić w C++ 03, jak również.

0

Aby rozwinąć na odpowiedź Rodrigo - Uważam, że to rozwiązanie, i pomyślałem, że warto wspomnieć:

struct True { unsigned char _[2]; operator bool() const { return true; } }; 
char is_forward_iterator(std::input_iterator_tag const *) { return 0; } 
True is_forward_iterator(std::forward_iterator_tag const *) { return True(); } 

Teraz, jeśli chcesz sprawdzić go wewnątrz jakiejś funkcji, można powiedzieć:

if (is_forward_iterator(static_cast<iterator_traits<It>::iterator_category*>(0))) 
{ 
    ... 
} 

i jeśli chcesz sprawdzić go w szablonach, można sprawdzić na:

sizeof(
    is_forward_iterator(static_cast<iterator_traits<It>::iterator_category*>(0)) 
) > 1 

z podstawowym Zaletą tej metody jest to, że unika deklarowania szablonów (np. dla lepszej prędkości kompilacji).

Powiązane problemy