Ich mag würde einen Konstruktor für MyClass
, die ein Argument, um schreiben, und ich will dies nur kompilieren, wenn das Argument ein pointer
oder ein iterator
(etwas mit iterator_traits
). Wie erreiche ich das?std :: enable_if oder SFINAE für Iterator oder Zeiger
Antwort
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).
- 1. SFINAE: std :: enable_if als Funktionsargument
- 2. Erhalten Iterator von Zeiger oder Referenz
- 3. Warum funktioniert SFINAE (enable_if) nicht für Elementfunktionen einer Klassenvorlage?
- 4. C++ std :: Iterator ohne intern: std :: vector oder std :: list
- 5. SFINAE funktioniert nicht auf Visual Studio 2010 für std :: is_pointer
- 6. 'if' mit Template-Parametern oder SFINAE wird bevorzugt? diese
- 7. std :: konditional vs std :: enable_if
- 8. SFINAE geschieht nicht mit std :: underlying_type
- 9. variable Vorlagen und std :: enable_if
- 10. Schwache Referenzen oder Zeiger
- 11. Declval Ausdruck (für SFINAE) mit Std :: Ostream
- 12. Zeiger als C++ Iterator
- 13. Barton-Nackman vs std :: enable_if
- 14. g ++ und clang ++ unterschiedliches Verhalten bei SFINAE und SFINAE Fehler
- 15. C++ 11 bewegen Einfügung für std :: deque oder std :: list
- 16. Std :: Hash-Spezialisierung mit SFINAE?
- 17. Sollte meine Funktion einen Zeiger auf std :: vector oder einen Verweis auf std :: vector zurückgeben?
- 18. Kann nicht bekommen SFINAE
- 19. Iterieren mit auto ref oder Iterator
- 20. SFINAE: 'static_cast <void>()' oder ', void()'?
- 21. Std :: enable_if: Parameter vs Vorlage Parameter
- 22. Referenz oder Zeiger auf std :: vector unvollständigem Typ
- 23. sfinae Prüfung für statische Mitglied mit declltype
- 24. Aktivieren Sie Konvertierungsoperator mit SFINAE
- 25. Java Iterator nextindex Zeiger
- 26. Beziehung oder Zeiger?
- 27. Verständnis SFINAE
- 28. std :: enable_if ein Argument ist eine Funktion?
- 29. Verwenden von sizeof ... in std :: enable_if
- 30. Std :: Vector Iterator Invalidation
@LucDanton stimmte zu, ich habe die SFINAE als bevorzugte Technik eingesetzt. – ecatmur
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. –
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'.) –