C++ almacenando estructuras de plantillas

I have a base structure FooBase:

struct FooBase { };

Then I create a template structure Foo que es un hijo de FooBase:

template <typename typeName> struct Foo : public FooBase { typeName* foo };

In some class I create a vector of FooBase and add instances of Foo en eso:

vector <FooBase> FooVector
...    
Foo <Bar> fooInstance;
fooInstance.foo = new Bar();
FooVector.push_back ( fooInstance );

Then I needed to access the stored data, but I'm getting predictable and obvious error about an absence of the member foo in FooBase

FooVector[0].foo

I can not write something like

Foo <Bar> fooInstance = FooVector[0]

since I don't know the template parameter.

How do I store instances of Foo in the vector so I can access them later. Note, that I don't know the template parameter at the last step - when reading data from the vector.

P.S. NO BOOST ALLOWED!

preguntado el 28 de agosto de 12 a las 09:08

can you just refer to Bar by typename? Do you need to do anything with Bar that is specific to it's type (my guess would be no). -

What you're looking for is a technique called type erasure. -

3 Respuestas

What happens here, is that in your

FooVector.push_back ( fooInstance );

line, C++ silently invokes the copy constructor of FooBase, because you can only keep objects of that type in your vector. Since Foo hereda públicamente de FooBase el método FooBase::FooBase(FooBase const&) pueden be called with an object of type Foo.

So, you're not really storing Foos, but in fact FooBases. To do what you want to do you need an std::vector<FooBase*> or std::vector<std::shared_ptr<FooBase> >.

However, the contents of your vector, will aun falta un foo member, because the static type is still not Foo. To get around this, you have some options. You podría dynamic_cast or static_cast su FooBase* post-extracción Foo* y luego acceder a su foo member. But that could break, since the FooBase* pointers might actually hold another type than Foo.

¿Por qué no usas un std::vector<Foo<Bar> > ¿en lugar?

Respondido 28 ago 12, 09:08

Why don't you just use an std::vector<Foo<Bar> > instead? Because I do not know the template parameter! Bar is just an example. - Kolyunya

@Kolyunya: Then how can you possibly expect .foo to give you the right type? - máscara de bits

That was my question. How to store a vector of template structures with template parameter which I do not know. - Kolyunya

@Kolyunya: This doesn't make sense to me. Why would you try to extract an object which you don't even know the type of? Perhaps you could clarify your question. As it stands, I don't get it -- and apparently no one else. - máscara de bits

@Kolyunya: To access a member of a type X you need to know the full type X. No way around it. If X happens to be a template Y<Z> then you have to know which Z va con el Y. How should C++ know what .foo se supone que significa? - máscara de bits

You are slicing here:

vector <FooBase> FooVector
...    
Foo <Bar> fooInstance;
fooInstance.foo = new Bar();
FooVector.push_back ( fooInstance );

Estás empujando un Foo<Bar> en un vector de FooBase so you only get a FooBase object stored. This

FooVector[0]

devuelve una referencia a un FooBase which knows nothing about Foo<Bar>. You could store Foo<Bar> directamente:

vector<Foo<Bar>> FooVector;

or store pointers or smart pointers to FooBase, but then you will have to dynamic_cast the elements to Foo<Bar>* anyway, which isn't a very nice solution.

Respondido 28 ago 12, 10:08

Yes, I know it. How can I store a vector of Foo? Does it change anything if I put a pointer-to-Foo in the vector of pointers-to-FooBase? - Kolyunya

@Kolyunya: A vector of pointers is one way to avoid slicing. However, it makes the vector's copy constructor and copy assignment do the wrong thing. It's one of the ugliest C++ problems, IMO. (Other ugly solutions include ptr_vector, boost::any, and vectors of smart pointers.) - David Schwartz

@Kolyunya I added some options to my answer. - juanchopanza

but then you will have to dynamic_cast the elements to Foo<Bar>* anyway, which isn't a very nice solution. I can not cast, because I don't know the value of template parameter. Bar es solo un ejemplo - Kolyunya

@Kolyunya: Then what could you possibly do with the returned object?! If you don't know its type, how would you even know which functions it supports or which data members it has? It sounds like you need to push any functionality you need to invoke on an object whose full type you don't know into the base class. - David Schwartz

As mentioned in previous answers, you are slicing Bar into FooBase objects. Your vector has to store references to Foo objects rather than the object itself.

The easiest way to accomplish this is to use one of the smart_ptr types from the boost library.

#include <boost/shared_ptr.hpp>
#include <vector>
typedef std::vector< boost::shared_ptr<FooBase> > FooVector;
...
FooVector v;
v.push_back( new Bar() );

If you store the pointer itself into the vector you'll have problems with memory management, using the boost shared_ptr type will handle allocation for you automatically.

Respondido 28 ago 12, 10:08

(You can also, of course, use smart pointer types for any other library or write your own, since you don't want to use Boost. The point is the semantics you need, not the implementation.) - David Schwartz

As David mentioned you don't have to use boost; however you SHOULD use some kind of smart pointer implementation for your vector references. And you MUST use reference types in your vector to avoid the slicing problem. Failure to use smart pointers places the burden on YOU to handle deallocation; which makes using some of the vector methods (i.e., erase) extremely dangerous. - Kevin Sitzé

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.