me gustaría escribir un constructor para MyClass
que tienen un argumento y yo quiero que esto compilar sólo si el argumento es un pointer
o un iterator
(algo que tiene iterator_traits
). ¿Cómo lograr esto?std :: enable_if o SFINAE de iterador o puntero
Respuesta
Lamentablemente, no hay una forma estándar para detectar si una clase modelos Iterator
. La comprobación más simple sería que *it
y ++it
son sintácticamente válidos; usted puede hacer esto utilizando técnicas estándar SFINAE:
template<typename T,
typename = decltype(*std::declval<T&>(), void(), ++std::declval<T&>(), void())>
MyClass(T);
Teniendo en cuenta los requisitos de Iterator
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
)> {};
El problema con tratar de utilizar iterator_traits
es que es una plantilla definida para todos tipos, y su creación de instancias fallará en un contexto que no sea SFINAE (recuerde que SFINAE solo aplica para fallas de sustitución directa). libstdC++ tiene un conforming extension mediante el cual la instanciación de iterator_traits
en tipos no iteradores producirá un tipo vacío; se puede hacer un truco similar mediante la comprobación de la existencia de iterator_category
del tipo:
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);
Este será sin embargo no funcionar para los tipos que por sí mismas no exponer a iterator_category
pero han sido adaptadas por un iterator_traits
especialización separada; en ese caso, el método SFINAE simple tiene más sentido (y puede instanciar iterator_traits
dentro del constructor para confirmar que el tipo es similar a un iterador).
- 1. ¿Convertir iterador en puntero?
- 2. std :: map insert o std :: map find?
- 3. std :: enable_if: parámetro vs template parámetro
- 4. iterador equivalente al puntero nulo?
- 5. Problema con SFINAE
- 6. iterador frente a referencia frente a puntero
- 7. Diferenciación SFINAE entre firmado y no firmado
- 8. índice o posición en std :: conjunto
- 9. Mezclar decltype y enable_if
- 10. std :: set iterador automáticamente const
- 11. m.find (...) == m.end() - que se usa, iterador o const_iterator
- 12. SFINAE compilador preocupa
- 13. sfinae busca miembro estático usando decltype
- 14. ¿Debo devolver un iterador o un puntero a un elemento en un contenedor STL?
- 15. Heredar std :: istream o equivalente
- 16. especialización del método enable_if
- 17. Cuándo usar `static_assert` en lugar de SFINAE?
- 18. boost :: enable_if no en la firma de función
- 19. Método de plantilla enable_if especialización
- 20. std :: string :: iterador para compensar y volver
- 21. std :: invalidación de iterador de vector
- 22. ¿Cómo funciona el iterador std :: map?
- 23. C++ 11 error enable_if
- 24. cómo detectar si un tipo es un iterador o const_iterator
- 25. enable_if + disable_if combinación provoca una llamada ambigua
- 26. usando SFINAE para la plantilla de la especialización de clase
- 27. iterador avanzado para el std :: vector std :: advance VS operator +?
- 28. Parámetros de función: ¿Copia o puntero?
- 29. Iterador al último elemento en std :: list
- 30. ¿Hay algo como "std :: and" o "std :: or"?
@LucDanton estuvo de acuerdo, he puesto el SFINAE como una técnica preferida. – ecatmur
Gran respuesta, me salvaste explicando la extensión que usamos en libstdC++ :) Consulta http://gcc.gnu.org/bugzilla/show_bug.cgi?id=40497 para ver el fondo de esa extensión. El mérito de la idea de verificar por 'iterator_category' va para Alisdair Meredith. –
Mmmh, los requisitos de '* it' es que el tipo sea' std :: iterator_traits :: reference'; no es que sea un tipo de referencia (al menos para Iterator). Pero no puedes usar 'std :: iterator_traits' por miedo a arruinar SFINAE ... ¡Te dejaré arreglar eso! (También tiene problemas de nuevo con la categoría de valor de algunas expresiones que prueba; por ejemplo, '++ std :: declval ()', no 'T'.) –