chciałbym napisać konstruktora dla MyClass
że pobiera argumentu, a ja chcę to do kompilacji tylko jeśli argument jest pointer
lub iterator
(coś mające iterator_traits
). Jak to osiągnąć?std :: enable_if lub SFINAE dla iteratora lub wskaźnik
Odpowiedz
Niestety, nie ma standardowego sposobu wykrywania, czy modele klasy Iterator
. Najprostszym sprawdzeniem jest, że *it
i ++it
są poprawne pod względem składni; można to zrobić przy użyciu standardowych technik SFINAE:
template<typename T,
typename = decltype(*std::declval<T&>(), void(), ++std::declval<T&>(), void())>
MyClass(T);
Uwzglę wymogów Iterator
z 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
)> {};
Problem z próby użycia iterator_traits
jest to, że jest zdefiniowany szablon dla wszystkich typy i ich instancja zakończy się niepowodzeniem w kontekście innym niż SFINAE (należy pamiętać, że SFINAE ma zastosowanie wyłącznie w przypadku bezpośredniej awarii substytucji). libstdC++ ma numer conforming extension, w którym tworzenie wystąpień iterator_traits
na typach innych niż iteratory spowoduje utworzenie pustego typu; można zrobić podobną sztuczkę poprzez sprawdzenie istnienia iterator_category
na temat typu:
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);
Będzie to jednak nie działać dla typów, które same nie narażają iterator_category
ale zostały dostosowane przez oddzielny iterator_traits
specjalizacji; w takim przypadku prosta metoda SFINAE ma więcej sensu (i można utworzyć instancję iterator_traits
w konstruktorze, aby potwierdzić, że typ jest podobny do iteratora).
- 1. SFINAE enable_if jawny konstruktor
- 2. Dlaczego void_t robi praca w SFINAE ale enable_if robi
- 3. C++ - std :: enable_if dla kilku rodzajów
- 4. SFINAE nie dzieje z std :: underlying_type
- 5. Barton-Nackman kontra std :: enable_if
- 6. SFINAE constexpr ze std :: get
- 7. Dlaczego funkcja SFINAE (enable_if) nie działa dla funkcji składowych szablonu klasy?
- 8. g ++ i clang ++ inne zachowanie z błędem SFINAE i SFINAE
- 9. SFINAE: 'static_cast <void>()' lub ', void()'?
- 10. mogę uzyskać wskaźnik do aktualnej wartości iteratora
- 11. Jak uzyskać indeks tablicy lub numer iteracji dla każdego iteratora?
- 12. Czy moja funkcja powinna zwrócić wskaźnik do std :: vector lub odwołanie do std :: vector?
- 13. Należy użyć std :: set lub std :: unordered_set dla zestawu wskaźników?
- 14. Nie można uzyskać SFINAE pracować
- 15. Parametry funkcji: Kopiuj lub wskaźnik?
- 16. boost :: enable_if nie w sygnaturze funkcji
- 17. Zrozumieć SFINAE
- 18. std :: enable_if Z parametrami szablonu bez szablonu
- 19. C++ std :: enable_if warianty ograniczające i problemy
- 20. sfinae away a destructor
- 21. warunkowy (SFINAE) przesłanianie
- 22. std :: remove_reference lub std :: remove_cv first?
- 23. Używaj zwykłego iteratora do iteracji wstecznej lub zmagań z reverse_iterator?
- 24. umożliwić operatorowi konwersji przy użyciu SFINAE
- 25. SFINAE rozróżnienie między znakiem i unsigned
- 26. przekazać tablicę do zawiniętej funkcji jako wielkości wskaźnik + lub zakresu
- 27. Sprawdzanie sfinae dla statycznego elementu przy użyciu dekltype
- 28. Dziedziczenie std :: istream lub równoważnego
- 29. Adapter przejściowy dla iteratora wskaźników
- 30. Zastępowanie obiektu std :: list z podaniem iteratora
@LucDanton zgodził się, umieściłem SFINAE jako preferowaną technikę. – ecatmur
Świetna odpowiedź, uratowałaś mnie wyjaśniając rozszerzenie, którego używamy w libstdC++ :) Zobacz http://gcc.gnu.org/bugzilla/show_bug.cgi?id=40497 dla tła tego rozszerzenia. Pomysł na sprawdzenie "iterator_category" trafił do Alisdair Meredith. –
Mmmh, wymaganiami '* it' jest to, że typem jest' std :: iterator_traits :: reference'; nie oznacza to, że jest to typ odniesienia (przynajmniej dla Iteratora). Ale nie możesz użyć 'std :: iterator_traits' w obawie przed zniszczeniem SFINAE ... Pozwolę ci to naprawić! (Ponownie masz problemy z kategorią wartości niektórych wyrażeń, które testujesz, np. '++ std :: declval ()', a nie 'T'.) –