Hi,
I have a template class, C_Foo<T>, which is specialised in a number of ways.
struct Bar_Base { ... };
struct Bar_1 : public Bar_Base { ... };
struct Bar_2 : public Bar_Base { ... };
struct Bar_3 : public Bar_Base { ... };
class C_Foo<T> { ... };
class C_Foo_1 : public C_Foo<Bar_1> { ... };
class C_Foo_2 : public C_Foo<Bar_2> { ... };
class C_Foo_3 : public C_Foo<Bar_3> { ... };
And instantiations as follows:
C_Foo_1 foo1;
C_Foo_2 foo2;
C_Foo_3 foo3;
I have a set of common operations, all of which are defined on C_Foo, that I want to perform on foo1, foo2, and foo3. I've tried the following:
vector<C_Foo *> v;
v.push_back(&foo1);
v.push_back(&foo2);
v.push_back(&foo3);
But I get compile errors, presumably because the compiler isn't sure how to go from a C_Foo_1 to a C_Foo.
Is it possible to do something like this? I want to be able to loop through foo1 .. fooN and perform the same operations on all of them, without having to copy and paste boilerplate code like so:
foo1.do_stuff();
foo2.do_stuff();
foo3.do_stuff();
Thanks for your help.
-
The problem is that
C_Foo
cannot be instantiated because it requires template parameters.You can make another base class, and have your set of common operations within that:
class C_FooBase { ... }; template<typename T> class C_Foo<T> : public C_FooBase { ... }; class C_Foo_1 : public C_Foo<Bar_1> { ... }; class C_Foo_2 : public C_Foo<Bar_2> { ... }; class C_Foo_3 : public C_Foo<Bar_3> { ... };
-
You can do that, if the function does not depend on the template parameter:
// note: not a template class C_Foo_Common { public: virtual void do_stuff() = 0; }; template<typename T> class C_Foo : public C_Foo_Common { virtual void do_stuff() { // do stuff... } }; vector<C_Foo_Common *> v; v.push_back(&foo1); v.push_back(&foo2); v.push_back(&foo3); // now, you can iterate and call do_stuff on them.
But if the function in C_Foo_Common needs to know the type
T
(for example to have another return type that depends on T), then that's not possible anymore.C_Foo<Bar_1>
is a different type thanC_Foo<Bar_2>
. You can use discriminated unions instead. Those keep track about what is stored in them and are completely generic:typedef boost::variant< C_Foo<Bar_1>*, C_Foo<Bar_2>*, C_Foo<Bar_3>* > variant_type; vector<variant_type> v; v.push_back(&foo1); v.push_back(&foo2); v.push_back(&foo3);
The variant knows what it stores, and can call functions overloaded on the types of what can be stored in it. Read the documentation of
boost::variant
for more information on how to get at what the variants contain. -
This is because C_Foo<T> does not exist as a class. C_Foo<Bar1> is the real class and has nothing to do with a C_Foo<Bar2>, these are totally different classes. A template is a tool for the compiler to duplicate code, it is not a real class like generics can be in Java or C#.
If you look again at your code by thinking that C_Foo<Bar1>, C_Foo<Bar2> and C_Foo<Bar3> are totally different classes and have nothing in common, then you understand why you can't do what you want the way you want.
Like strager said, just use a real base class as the common class
0 comments:
Post a Comment