biblioteca array c++
Comprobar si una variable es iterable? (5)
Depende de lo que quieras decir con "iterable". Es un concepto suelto en C ++, ya que podría implementar iteradores de muchas maneras diferentes.
Si por foreach
se refiere a los bucles basados en el rango de C ++ 11, el tipo necesita los métodos begin()
y end()
que deben definirse y devolver los iteradores que responden a operator!=
, operator++
y operator*
.
Si te refieres al ayudante BOOST_FOREACH de Boost, entonces ve Extensibilidad de BOOST_FOREACH .
Si en su diseño tiene una interfaz común de la cual se heredan todos los contenedores iterables, entonces podría usar std::is_base_of C ++ 11:
struct A : IterableInterface {}
struct B {}
template <typename T>
constexpr bool is_iterable() {
return std::is_base_of<IterableInterface, T>::value;
}
is_iterable<A>(); // true
is_iterable<B>(); // false
¿Hay alguna forma de verificar si un tipo de variable arbitraria es iterable?
Entonces, ¿para comprobar si tiene elementos indexados o si realmente puedo pasar por encima de sus hijos? (Use foreach por ejemplo?)
¿Es posible crear una plantilla universal para eso?
He encontrado técnicas para otros lenguajes de programación mientras la buscaba. Sin embargo, todavía tengo que descubrir cómo hacer esto en C ++.
No es posible verificar si un tipo de variable arbitraria es iteratable. Sin embargo, hay algunos tipos comunes para los cuales se podría hacer esto, pero la verificación sería en tiempo de compilación y no en tiempo de ejecución.
Los contenedores de estilo STL en c ++ exponen los miembros comienzan y terminan. La presencia de estos miembros podría utilizarse para determinar que un tipo es probable que se pueda iterar.
El tipo de matriz primitiva en c ++ introduce un poco de un obstáculo. La dirección del primer índice válido y la dirección que sigue al último índice válido se pueden utilizar como iteradores de inicio y fin válidos. Sin embargo, las matrices c ++ no exponen su longitud; necesitas almacenar eso en una variable separada. Por lo tanto, para determinar el "iterador final" de una matriz, necesita dos partes de información: la dirección del primer índice y la longitud de la matriz. (En realidad, también necesita una tercera pieza de información: el ancho de bytes de cada índice, pero esto generalmente se puede derivar de la propia matriz).
Puedes crear un rasgo para eso:
namespace detail
{
// To allow ADL with custom begin/end
using std::begin;
using std::end;
template <typename T>
auto is_iterable_impl(int)
-> decltype (
begin(std::declval<T&>()) != end(std::declval<T&>()), // begin/end and operator !=
void(), // Handle evil operator ,
++std::declval<decltype(begin(std::declval<T&>()))&>(), // operator ++
void(*begin(std::declval<T&>())), // operator*
std::true_type{});
template <typename T>
std::false_type is_iterable_impl(...);
}
template <typename T>
using is_iterable = decltype(detail::is_iterable_impl<T>(0));
cpprefence tiene un ejemplo que responde a su pregunta . Está utilizando SFINAE , aquí hay una versión ligeramente modificada de ese ejemplo (en caso de que el contenido de ese enlace cambie con el tiempo):
template <typename T, typename = void>
struct is_iterable : std::false_type {};
// this gets used only when we can call std::begin() and std::end() on that type
template <typename T>
struct is_iterable<T, std::void_t<decltype(std::begin(std::declval<T>())),
decltype(std::end(std::declval<T>()))
>
> : std::true_type {};
// Here is a helper:
template <typename T>
constexpr bool is_iterable_v = is_iterable<T>::value;
Ahora, así es como puede ser usado.
std::cout << std::boolalpha;
std::cout << is_iterable_v<std::vector<double>> << ''/n'';
std::cout << is_iterable_v<std::map<int, double>> << ''/n'';
std::cout << is_iterable_v<double> << ''/n'';
struct A;
std::cout << is_iterable_v<A> << ''/n'';
Salida:
true
true
false
false
Dicho esto, todo lo que comprueba es que la declaración de begin() const
y end() const
, por lo tanto, incluso lo siguiente se verifica como iterable:
struct Container
{
void begin() const;
void end() const;
};
std::cout << is_iterable_v<Container> << ''/n''; // prints true
Pueden ver estas piezas juntas here
Sí usando esta clase de rasgos compatible c ++ 03
template<typename C>
struct is_iterable
{
typedef long false_type;
typedef char true_type;
template<class T> static false_type check(...);
template<class T> static true_type check(int,
typename T::const_iterator = C().end());
enum { value = sizeof(check<C>(0)) == sizeof(true_type) };
};
Explicación
-
check<C>(0)
callscheck(int,const_iterator)
siC::end()
existe y devuelve un tipo compatible conconst_iterator
- else
check<C>(0)
callcheck(...)
(vea la conversión de puntos suspensivos ) -
sizeof(check<C>(0))
depende del tipo de retorno de estas funciones - Finalmente, el compilador establece el
value
constante entrue
ofalse
Ver compilación y prueba de ejecución en coliru
#include <iostream>
#include <set>
int main()
{
std::cout <<"set="<< is_iterable< std::set<int> >::value <<''/n'';
std::cout <<"int="<< is_iterable< int >::value <<''/n'';
}
Salida
set=1
int=0
Nota: C ++ 11 (y C ++ 14) proporciona muchas clases de rasgos, pero ninguna sobre iterabilidad ...
Ver también respuestas similares de jrok y Jarod42 .
Esta respuesta está en Dominio Público - CC0 1.0 Universal