2012-07-06 13 views
6

Estoy diseñando una clase simple Array con la capacidad de sostener cualquier tipo de objeto, como un vector que puede contener múltiples tipos de datos en un objeto. (Esto es para fines de aprendizaje.)¿Cómo obtengo el tipo de plantilla de un elemento determinado en tiempo de ejecución en C++?

tengo una clase vacía base llamada Container:

class Container {}; 

Y una subclase a plantillas llamada Object:

template <class T> 
class Object : public Container { 
    T& object; 
public: 
    Object(T& obj = nullptr) : object(obj) {} 
}; 

tengo una clase Array que tiene una vector de punteros a Container s que utilizo para contener Object s:

class Array { 
    std::vector<Container *> vec; 
public: 
    template <class T> 
    void add_element(const T&); 
    auto get_element(int); 
}; 

add_element elementos almacena en Object s y las pone en vec:

template <class T> 
void Array::add_element(const T& element) 
{ 
    vec.push_back(new Object<T>(element)); 
} 

get_element elimina el elemento del que es Object y pasa de nuevo a la persona que llama. Aquí es donde reside mi problema. Con el fin de borrar un elemento de la Object, necesito saber qué tipo de Object es:

auto Array::get_element(int i) 
{ 
    return (Object</* ??? */> *)vec[i])->object; 
} 

¿Hay alguna manera para mí para encontrar qué tipo de objeto Estoy almacenando?

Editar: Dado que las personas afirman que esto no es posible, ¿qué tal esto. ¿Hay alguna manera de almacenar realmente información de tipo dentro de una clase? (Sé que puedes hacer eso en ruby). Si pudiera hacer eso, podría almacenar el tipo de devolución de get_element en cada Object.

+0

@ildjarn: Desde el PO está usando 'auto' en la pregunta, creo que es seguro asumir que. –

+0

@ user315052 porque sería más difícil de usar de esa manera. @ildjarn Sí, lo hace, pero si 'decltype (vec [i])' obtendré 'Container *'. Necesito saber qué subclase es. – anthropomorphic

+0

@Jesse: Ah, buen punto. :-P – ildjarn

Respuesta

2

Estaba pensando algo así como esta plantilla para su get_element.

template <class T> 
T& Array::get_element(int i, T &t) 
{ 
    Object<T> *o = dynamic_cast<Object<T> *>(vec[i]); 
    if (o == 0) throw std::invalid_argument; 
    return t = o->object; 
} 

Array arr; 
double f; 
int c; 

arr.get_element(0, f); 
arr.get_element(1, c); 

Pero, alternativamente, se puede usar esto:

template <class T> 
T& Array::get_element(int i) 
{ 
    Object<T> *o = dynamic_cast<Object<T> *>(vec[i]); 
    if (o == 0) throw std::invalid_argument; 
    return o->object; 
} 

f = arr.get_element<double>(0); 
c = arr.get_element<int>(1); 
+0

Sabes, creo que esta es la mejor manera, gracias. – anthropomorphic

+0

No me gusta mucho tu declaración general, generalmente arrojaría una 'std :: exception', pero tu idea básica es buena. .No prb - thx =) – anthropomorphic

+1

@MichaelDorst: Edit made, saludos – jxh

1

El tipo de retorno de una función como Array::get_element se debe determinar en tiempo de compilación. Dado que la clase Array no sabe qué tipos de contenedores almacena en tiempo de compilación, la única opción es simplemente devolver el puntero Container* base. Entonces los usuarios de ese código pueden usar funciones virtuales y/o dynamic_cast para tratar con esos contenedores generalizados.

+0

El objetivo de esta clase era no tener que usar toda esa sintaxis cada vez que quisiera acceder a un elemento. '((Objeto *) vec [num] .-> object' es demasiado complicado – anthropomorphic

+1

Si' Container' tiene suficientes funciones virtuales, no necesita ningún molde. – aschepler

1

dynamic_cast <>(), cuando se utiliza en un puntero, se puede utilizar para comprobar si el molde es realmente posible ya que devuelve NULL cuando los tipos no coinciden.

Sin embargo, es probable que desee reconsiderar su configuración; dynamic_cast no es algo que querrás usar si puedes evitarlo, y si tienes cascadas de "if object-is-type1" haz esto; si else object-is-type2 hazlo; else si object-is-type3 ... "casi siempre es una señal segura de mal diseño.

Usar el auto en su get_element es probablemente su truco para evitar el problema de que no sabe cuál será el tipo - aunque no funcionará. El compilador todavía tiene que saber el tipo de devolución; no puede cambiar en tiempo de ejecución.

EDITAR: también puede dar un paso atrás y preguntarse si REALMENTE necesita una matriz que pueda almacenar "cualquier cosa". He usado Objective-C durante mucho tiempo, y básicamente se basa en la idea de que todo es solo un "objeto" y la gente puede llamar cualquier cosa, todo está solucionado en tiempo de ejecución. Sin embargo, descubrí que los usos reales de esto son raros ...y para los contenedores en particular, encuentro que cada NSArray que uso solo tiene objetos de un tipo, o tal vez de una clase base común, pero en realidad no son clases distintas, no relacionadas.

+0

Los usos son raros, pero aún así es necesario tener esa capacidad. Mi idea original era crear una clase que pudiera usar como envoltorio alrededor de objetos Obj-C (para minimizar el código Obj-C++ ya que es difícil de mantener). Si utilizo un vector no puedo manejar todos los escenarios , lo cual es problemático. – anthropomorphic

Cuestiones relacionadas