Reputation: 6821
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
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
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
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