C++ almacenando estructuras de plantillas
Frecuentes
Visto 419 veces
0
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!
3 Respuestas
2
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 Foo
s, but in fact FooBase
s. 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
1
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
0
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é
can you just refer to
Bar
bytypename
? Do you need to do anything withBar
that is specific to it's type (my guess would be no). - MartyEWhat you're looking for is a technique called type erasure. - Ylisar