Pure Virtual Methods with Different Datatypes

2019-06-14 03:51发布

问题:

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 some 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.

回答1:

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.



回答2:

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;
};


回答3:

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.



回答4:

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.



回答5:

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



回答6:

#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.