I'm making a base class for my container classes to derive from so I can maintain a consistent interface. It currently looks something like this:
template <typename Datatype>
class BaseClass
{
public:
virtual Datatype Foo() = 0;
virtual Datatype Bar() = 0;
};
template <typename Datatype>
class DerivedClass: public BaseClass<Datatype>
{
public:
virtual Datatype Foo()
{
}
virtual Datatype Bar()
{
}
};
However, with some of my derived classes, Foo()
and Bar()
may need to have different return types from each other. Without having a template parameter for each different return type in the base class, how can I give the derived classes s开发者_运维技巧ome room for changing this sort of thing?
EDIT:
The types the derived classes use are potentially completely different and invariant. Really, the derived classes aren't guaranteed to have any sort of common ground other than the method names.
Provide a trait which would be specialized in the cases where you need different result.
template <typename Datatype>
struct BaseTraits
{
typedef Datatype FooResult;
typedef Datatype BarResult;
};
template <typename Datatype, typename Traits = BaseTraits<Datatype> >
class BaseClass
{
public:
virtual Traits::FooResult Foo() = 0;
virtual Traits::BarResult Bar() = 0;
};
If you know the number of potential types ahead of time you can extend what you've got by adding additional types to the base class template...
template <typename FOO_TYPE,typename BAR_TYPE>
class BaseClass
{
public:
virtual FOO_TYPE Foo() = 0;
virtual BAR_TYPE Bar() = 0;
};
template <typename FOO_TYPE,typename BAR_TYPE>
class DerivedClass: public BaseClass<FOO_TYPE,BAR_TYPE>
{
public:
virtual FOO_TYPE Foo() { }
virtual BAR_TYPE Bar() { }
};
This could get out of hand rapidly if you have more than a few types.
It's NOT possible to return
different data types. The only way would have been to make the methods template
and that is restricted because virtual
methods can not be templates.
If the return types are co-variant, they can be changed, or you can write some kind of conversion function and have like, a real_bar
or something like that.
traits may help. The C++ Templates - The Complete Guide book provides an example that illustrates this in the chapter entitled Traits and Policy Classes. It has an example that uses an accumulator to return different types.
EDIT: I can see AProgrammer has given an example already
#include <iostream>
#include <typeinfo>
using namespace std;
template <typename T, typename R = T>
class base
{
public:
virtual R foo()
{
cout << "foo(): data type = " << typeid(T).name() << endl; return R();
}
virtual R bar()
{
cout << "bar(): return type = " << typeid(R).name() << endl; return R();
}
};
int main()
{
base<int> b;
base<int, long> b1;
b.foo(); b.bar();
b1.foo(); b1.bar();
cout << typeid(b).name() << endl;
cout << typeid(b1).name() << endl;
return 0;
}
The above program returned the following:
With g++
:
$ ./rettypetemplate
foo(): data type = i
bar(): return type = i
foo(): data type = i
bar(): return type = l
4baseIiiE
4baseIilE
With Visual Studio 2005 cl.exe
:
C:\Program Files\Microsoft Visual Studio 8\VC>rettypetemplate.exe
foo(): data type = int
bar(): return type = int
foo(): data type = int
bar(): return type = long
class base<int,int>
class base<int,long>
Though this sample shows how to do it, the answer given by AProgrammer shows a good way of handling it using traits. The above example will work well for fundamental data types but not for user defined types. To handle user defined types, traits is the way to go. Refer to either the "The C++ Template" book by Jossutis or "Modern C++ design" to know more about traits.
精彩评论