2012-08-28 11 views

Antwort

11

Leider gibt es keinen Standard Weg zu erkennen, ob eine Klasse Modelle Iterator. Die einfachste Überprüfung wäre, dass *it und ++it beide syntaktisch gültig sind; Sie können dies unter Verwendung von Standard-Techniken SFINAE tun:

template<typename T, 
    typename = decltype(*std::declval<T&>(), void(), ++std::declval<T&>(), void())> 
    MyClass(T); 

Unter Berücksichtigung der Iterator Anforderungen von 24.2.2: 2:

template<typename T> typename std::enable_if< 
    !std::is_void<decltype(*std::declval<T &>())>::value 
    && std::is_same<decltype(++std::declval<T &>()), 
        typename std::add_lvalue_reference<T>::type>::value, 
    std::true_type>::type has_iterator_requirements_helper(int); 
template<typename T> std::false_type has_iterator_requirements_helper(...); 
template<typename T> struct has_iterator_requirements: 
    decltype(has_iterator_requirements_helper<T>(0)) {}; 

template<typename, bool> struct is_iterator_check: std::false_type {}; 
template<typename T> struct is_iterator_check<T, true>: std::true_type { 
    typedef typename std::iterator_traits<T>::difference_type difference_type; 
    typedef typename std::iterator_traits<T>::value_type value_type; 
    typedef typename std::iterator_traits<T>::iterator_category iterator_category; 
    typedef typename std::iterator_traits<T>::reference reference; 
    typedef typename std::iterator_traits<T>::pointer pointer; 
    static_assert(std::is_same<decltype(*std::declval<T &>()), reference>::value 
     || std::is_void<reference>::value, "*r must be of type reference"); 
}; 
template<typename T> struct is_iterator: is_iterator_check<T, 
    (std::is_pointer<T>::value 
    && !std::is_void<typename std::remove_pointer<T>::type>::value 
    && !std::is_function<typename std::remove_pointer<T>::type>::value 
    ) || (std::is_copy_constructible<T>::value 
    && std::is_copy_assignable<T>::value 
    && std::is_nothrow_destructible<T>::value 
    // TODO: check lvalues are swappable 
    && has_iterator_requirements<T>::value 
    )> {}; 

Das Problem mit dem Versuch iterator_traits zu verwenden ist, dass es sich um eine Vorlage für alle definiert ist Typen und deren Instanziierung wird in einem Nicht-SFINAE-Kontext fehlschlagen (daran erinnern, dass SFINAE nur für direkte Substitutionsfehler gilt). libstdC++ hat eine conforming extension, wobei die Instanziierung von iterator_traits auf Nicht-Iterator-Typen einen leeren Typ erzeugt; Sie können durch die Überprüfung für die Existenz von iterator_category auf der Art, die einen ähnlichen Trick:

template<typename T> std::true_type has_iterator_category_helper(
    T::iterator_category *); 
template<typename T> std::false_type has_iterator_category_helper(...); 
template<typename T> struct has_iterator_category<T>: 
    decltype(has_iterator_category_helper<T>(0)) { }; 
template<typename T> struct is_iterator: std::integral_constant<bool, 
    std::is_pointer<T>::value || has_iterator_category<T>::value> {}; 

template<typename T, typename = std::enable_if<is_iterator<T>::value>> 
    MyClass(T); 

Dies wird jedoch nicht für Typen arbeiten, die sie iterator_category nicht aussetzen, sondern werden von einer separaten iterator_traits Spezialisierung angepasst; In diesem Fall macht die einfache SFINAE-Methode mehr Sinn (und Sie können iterator_traits im Konstruktor instanziieren, um zu bestätigen, dass der Typ iteratorartig ist).

+0

@LucDanton stimmte zu, ich habe die SFINAE als bevorzugte Technik eingesetzt. – ecatmur

+5

Große Antwort, Sie speicherten mir die Erweiterung, die wir in libstdC++ verwenden :) Siehe http://gcc.gnu.org/bugzilla/show_bug.cgi?id=40497 für den Hintergrund dieser Erweiterung. Der Gedanke, nach "iterator_category" zu suchen, geht an Alisdair Meredith. –

+0

Mmmh, die Anforderungen von '* it' ist, dass der Typ' std :: iterator_traits :: reference' ist; nicht, dass es ein Referenztyp ist (zumindest für Iterator). Aber du kannst 'std :: iterator_traits' nicht benutzen, um nicht SFINAE zu vermasseln ... Ich werde dich das reparieren lassen! (Sie haben auch wieder Probleme mit der Wertkategorie einiger Ausdrücke, die Sie testen, z. B. '++ std :: declval ()', nicht 'T'.) –

Verwandte Themen