2010-01-22 16 views
6

Me preguntaba si C++ 0x ofrece alguna función integrada para comprobar si un paquete de parámetros de una plantilla variadic contiene un tipo específico. Hoy, boost ::: mpl :: contains se puede usar para lograr esto si está utilizando boost :: mpl :: vector como sustituto de las plantillas variadic adecuadas. Sin embargo, tiene un tiempo de compilación serio. Supongo que C++ 0x tiene soporte de compilación para std :: is_same. Así que estaba pensando si una generalización como a continuación también es compatible en el compilador.Comprueba si el paquete de parámetros C++ 0x contiene un tipo

template <typename... Args, typename What> 
struct is_present 
{ 
    enum { value = (What in Args...)? 1 : 0 }; 
}; 

Respuesta

3

No, usted tiene que utilizar la especialización (parcial) con plantillas variadic hacer en tiempo de compilación cálculos de este tipo:

#include <type_traits> 

template < typename Tp, typename... List > 
struct contains : std::true_type {}; 

template < typename Tp, typename Head, typename... Rest > 
struct contains<Tp, Head, Rest...> 
: std::conditional< std::is_same<Tp, Head>::value, 
    std::true_type, 
    contains<Tp, Rest...> 
>::type {}; 

template < typename Tp > 
struct contains<Tp> : std::false_type {}; 

Sólo hay una otra operación intrínseca para las plantillas variadic y que es la forma especial del operador sizeof que calcula la longitud de la lista de parámetros, por ejemplo:

template < typename... Types > 
struct typelist_len 
{ 
    const static size_t value = sizeof...(Types); 
}; 

Dónde estás recibiendo "tiene graves sobrecarga en tiempo de compilación" con impulsar mpl desde? Espero que no solo hagas suposiciones aquí. Boost mpl usa técnicas como la creación de instancias de plantillas perezosas para intentar reducir los tiempos de compilación en lugar de explotar como lo hace la meta-programación ingenua.

+0

Puede ser un conjunto más grande de operaciones intrínsecas debe ser compatible con el lenguaje/compilador que simplemente sizeof .... (4 puntos!) IMO, verificar la existencia es tan "fundamental" como encontrar el tamaño. Siento que mpl tiene una sobrecarga de rendimiento basada en esta prueba que escribí. http://www.dre.vanderbilt.edu/~sutambe/files/mpl_intersection.cpp Estoy usando algoritmo de intersección codificado a mano, así como la versión de MPL. g ++ 4.4 toma el mismo tiempo para compilar ambos. La versión de plantillas variables compila 10 veces más rápido. Por cierto, ¿pueden sugerirme algo de lectura sobre la técnica de creación de instancias de plantillas perezosas de mpl? – Sumant

+0

Encontré algunos buenos ejemplos de evaluación perezosa en el libro de metaprogramación de plantillas de C++. ¿No es obvio? Gracias de todos modos. – Sumant

+0

Sí, todo lo que tiene que hacer es evitar la creación de instancias de plantillas de meta-funciones (al exponer el tipo de alias de tipo anidado ") antes de dar el resultado a otras metafunciones de impulso. Las meta-funciones de Boost se diseñaron para evaluar meta-funciones en el último momento es necesario el alias de tipo anidado. También debe intentar y evitar los valores desnudos y usar los envoltorios del tipo de metadatos (como mpl :: bool_) porque también están diseñados para funcionar perezosamente. Algunas veces impulsar mpl proporciona dos formas de una meta-función, intente utilizar la que promueve la creación de instancias perezosas. –

2

Si desea evitar la recursión de tipo manual, std::common_type me parece ser la única utilidad en el STL que es una plantilla variadic, y por lo tanto la única que podría encapsular la recursión.


Solución 1

std::common_type encuentra el tipo menos derivado en un conjunto de tipos. Si identificamos números con tipos, específicamente números altos con tipos menos derivados, encuentra el mayor número en un conjunto. Luego, tenemos que asignar la igualdad al tipo de clave en un nivel de derivación.

using namespace std; 

struct base_one { enum { value = 1 }; }; 
struct derived_zero : base_one { enum { value = 0 }; }; 

template< typename A, typename B > 
struct type_equal { 
typedef derived_zero type; 
}; 

template< typename A > 
struct type_equal< A, A > { 
typedef base_one type; 
}; 

template< typename Key, typename ... Types > 
struct pack_any { 
enum { value = 
    common_type< typename type_equal< Key, Types >::type ... >::type::value }; 
}; 


Solución 2

Podemos cortar common_type un poco más. El estándar dice

Un programa puede especializarse este rasgo si al menos un parámetro de plantilla en la especialización es un tipo definido por el usuario.

y describe exactamente lo que contiene: un caso de especialización recursiva parcial, un caso que aplica un operador binario y un caso de terminal. Esencialmente, es una función genérica fold, y puede agregar cualquier operación binaria que desee. Aquí utilicé la suma porque es más informativo que OR. Tenga en cuenta que is_same devuelve integral_constant.

template< typename Addend > 
struct type_sum { // need to define a dummy type to turn common_type into a sum 
    typedef Addend type; 
}; 

namespace std { // allowed to specialize this particular template 
template< typename LHS, typename RHS > 
struct common_type< type_sum<LHS>, type_sum<RHS> > { 
    typedef type_sum< integral_constant< int, 
    LHS::type::value + RHS::type::value > > type; // <= addition here 
}; 
} 

template< typename Key, typename ... Types > 
struct pack_count : integral_constant< int, 
common_type< type_sum< is_same< Key, Types > > ... >::type::type::value > {}; 
+0

Este es un juego de ingenio. ¡Pero me gustó! Asume una buena comprensión del rasgo common_type. Tuve que cavar en el borrador público C++ 0x. Combinar eso con la conversión implícita a base_one a través de type_equal es inteligente. C++ tiene demasiados trucos ingeniosos. ¿Se puede hacer algo más intuitivo usando std :: is_same y logical, o de alguna manera? – Sumant

+0

@Sumant: Sí: v). – Potatoswatter

+0

Eso es algo mejor, pero los detalles que entumecen la mente deben ser internalizados. – Sumant

Cuestiones relacionadas