Ark-kun
Ark-kun

Reputation: 6821

Overriding functions (from two abstract base classes) that differ only by their return value

I want to create a class that implements two interfaces that have functions with a same name that differ only by their return values. How can I do this correctly?

template<class T>
class IBase
{
public:
    virtual T Method() = 0;
};

class Derived : public IBase<int>, public IBase<char> {
public:
    int _value;

    virtual int IBase<int>::Method();
    virtual char IBase<char>::Method();
};

int Derived::Method() {
    return _value;
}

char Derived::Method() {
    return _value;
}

Here are the errors that I get:

error C2555: 'Derived::Method: overriding virtual function return type differs and is not covariant from 'IBase<int>::Method

error C2556: 'int Derived::Method(void)' : overloaded function differs only by return type from 'char Derived::Method(void)'

error C2371: 'Derived::Method: redefinition; different basic types

error C2084: function 'char Derived::Method(void)' already has a body

In C# it's pretty easy to do this without any ambiguities using nearly same syntax (called explicit interface implementation):

class Derived : IBase<int>, IBase<char> {
    int _value;

    int IBase<int>.Method() {
        return _value;
    }

    char IBase<char>.Method();
        return _value;
    }
};

Explicitly implementations are private and thus cannot be used directly on variables of class Derived. They are still very usable though as you can cast the Derived to one of the interfaces to use the implementation:

var d = new Derived();
((IBase<int>)d).Method();

This can be rather useful. A class can implement ICanConvertTo many times to enable different conversions.

Upvotes: 3

Views: 2089

Answers (3)

user2249683
user2249683

Reputation:

With virtual in Derived

#include <iostream>

template<typename T>
class IBase
{
    public:
    virtual T method() = 0;
};

template<typename T>
class WrapBase : public IBase<T>
{
    protected:
    virtual T do_method(T*) = 0;

    public:
    virtual T method() {
        return do_method((T*)0);
    }
};

class Derived : public WrapBase<char>, public WrapBase<int>
{
    protected:
    virtual char do_method(char*) { return 'A'; };
    virtual int do_method(int*) { return 1; };
};

Removing virtual in Derived - Thanks to DyP:

include <iostream>

template<typename T>
class IBase
{
    public:
    virtual T method() = 0;
};

template<typename D, typename T>
class WrapBase : public IBase<T>
{
    public:
    virtual T method();
};

class Derived : public WrapBase<Derived, char>, public WrapBase<Derived, int>
{
    friend class WrapBase<Derived, char>;
    friend class WrapBase<Derived, int>;

    protected:
    char do_method(char*) { return 'A'; };
    int do_method(int*) { return 1; };
};

template<typename D, typename T>
inline T WrapBase<D, T>::method() {
    return static_cast<D*>(this)->do_method((T*)0);
}

Test:

int main () {
    Derived d;
    IBase<char>& c = d;
    IBase<int>& i = d;
    std::cout << c.method() << " != " << i.method() << std::endl;
}

Comment: Mixing static and dynamic polymorphism might be a bad design.

Upvotes: 4

Mats Petersson
Mats Petersson

Reputation: 129524

Function can not differ only by return value, because the compiler has no way to distinguish them. Consider:

long x;
Derived d;
x = d.Method();

both the char and the int variant are possible to convert to an long - which one should it use?

Edit: If you want to define conversions, the typical case is to define a cast-operator, e.g.

class X 
{
     float x;
   public:
     X(float f) : x(f) {}
     operator int() { return static_cast<int>(x); }
     operator char() { return static_cast<char>(x); }
     float getX() { return x; }
};

and then call it as:

X x(65.3);
int y = x;
char z = x;

cout << "x.f=" << x.getF() << " as char:" << z << " as int:" << y << endl;

Upvotes: 2

Jarod42
Jarod42

Reputation: 218268

Returned value type is not part of the function (method) signature.

So your two methods are seen as the same method (so the redefinition error).

So you can't do what you want. your method should have different signature.

Upvotes: 1

Related Questions