2008-11-19 25 views
22

Aunque dudo, tengo curiosidad acerca de si es posible extraer los parámetros de plantilla de tipo primitivo de un tipo existente, quizás usando RTTI.Extraer los parámetros de plantilla de C++

Por ejemplo:

typedef std::bitset<16> WordSet; 

¿Sería posible extraer el número 16 en el código anterior sin disco de codificación en otro lugar? Las implementaciones específicas del compilador son bienvenidas, aunque estoy particularmente interesado en g++.

Respuesta

37

No es posible. La forma habitual de hacerlo es la siguiente:

template<int N> 
struct foo { 
    static const int value = N; 
}; 

y para este tipo de

template<typename T> 
struct foo { 
    typedef T type; 
}; 

Puede acceder a ella a continuación como foo<39>::value o foo<int>::type.

Si usted tiene un tipo particular, puede utilizar parcial especialización de plantilla:

template<typename> 
struct steal_it; 

template<std::size_t N> 
struct steal_it< std::bitset<N> > { 
    static const std::size_t value = N; 
}; 

El mismo principio es posible que los parámetros de tipo también, por cierto. Ahora puede pasar cualquier conjunto de bits, como steal_it< std::bitset<16> >::value (tenga en cuenta que use size_t, no int!). Debido a que todavía no tenemos muchos parámetros de plantilla, tenemos que limitarnos a un conteo de parámetros en particular, y repetir las especializaciones de la plantilla de steal_it para contar desde 1 hasta N. Otra dificultad es explorar tipos que tienen parámetros mixtos (tipos y no tipos de parámetros). Probablemente esto no sea trivial de resolver.

Si usted no tiene el tipo, pero sólo un objeto de la misma, se puede utilizar un truco, que aún así obtener un valor en tiempo de compilación:

template<typename T> 
char (& getN(T const &))[steal_it<T>::value]; 

int main() { 
    std::bitset<16> b; 
    sizeof getN(b); // assuming you don't know the type, you can use the object 
} 

El truco es hacer que la plantilla de función automática deducir el tipo, y luego devolver una referencia a una matriz de caracteres. La función no necesita ser definida, lo único que se necesita es su tipo.

+0

Buen truco. Una cosa, sin embargo, comienzas diciendo que no es posible, ¡pero luego procedes a demostrar que sí! :) –

+0

@Richard Corden: Creo que estaba diciendo que no es posible hacerlo con RTTI – cdleary

+3

Richart Corden. Quise decir que generalmente no es posible seleccionar parámetros de plantilla arbitrarios. considere esto: plantilla clase, clase, tamaño_t> clase foo; ese truco no tendrá sus tipos :) –

2

En el caso de std::bitset, sólo puede utilizar la función de size() miembro de:

size_t sz = oh_my_word.size(); // sz is now 16 

En el caso general, se puede definir una función miembro que devuelve el tamaño de una forma similar:

template <int N> 
class Foo 
{ 
public: 
    int size() const { return N; } 
}; 
+0

Estoy hablando de extrayéndolo de clases de terceros con sin el método 'size()' ni nada similar, extrayéndolo simplemente conociendo el tipo en sí mismo. La razón es que no quiero tener que hacer una instancia ficticia solo para obtener el tamaño del conjunto de bits typedef'd. – cdleary

+0

No es una penalización particularmente mala para bitset, pero podría haber otros tipos en los que la creación de instancias de un tipo ficticio no es deseable o poco práctica. – cdleary

0

Puede hacer esto fácilmente en C++ 11 usando la deducción de argumentos y los contextos no evaluados (tenga en cuenta que la demostración utiliza la característica de plantilla variable de C++ 14 para mayor comodidad).

#include <type_traits> 
#include <iostream> 

template<int> 
struct foo {}; 

template<int arg_N> 
struct val { 
    static constexpr auto N = arg_N; 
}; 

template<template <int> typename T, int N> 
constexpr auto extract(const T<N>&) -> val<N>; 

template<typename T> 
constexpr auto extract_N = decltype(extract(std::declval<T>()))::N; 


int main() { 
    std::cout << extract_N<foo<5>>; 
} 

Live demo

0

Como se ha dicho en otras respuestas, por std::bitset puede obtener el tamaño mediante el uso de la función de miembro de size(), que debe ser la elección correcta, mejor que cualquier otro truco.

Había varias propuestas para el caso genérico, casi similar a la que yo propongo a continuación, pero aún así creo que éste es más simple:

template <template<std::size_t> typename T, std::size_t K> 
auto extractSize(const T<K>&) { 
    return K; 
} 

int main() { 
    std::bitset<6> f1; 
    std::bitset<13> f2; 
    std::cout << extractSize(f1) << std::endl; 
    std::cout << extractSize(f2) << std::endl; 
} 
Cuestiones relacionadas