Reputation: 370
Let's say I have Base and Derived classes:
class Base {
public:
virtual auto DoSomething(const Base&) const -> decltype(*this) = 0;
};
class Derived : public Base {
public:
const Derived& DoSomething(const Base&) const override; // OK, but not what I want
// Derived DoSomething(const Base&) const override; // ERROR, but I want this
};
The uncommented code compiles fine, and works fine if you fill it out. But I don't want to return a const reference from DoSomething()
. Is there any way to strip the modifiers from the trailing return type in the base class declaration? I've tried a few methods from <type_traits>
(remove_cvref_t
, etc), but they seem to evaluate the type to Base
, which creates a conflicting return type.
If what I'm trying to do is not possible, why is it not possible?
Upvotes: 1
Views: 89
Reputation: 22176
As you noticed, you can make make Base::DoSomething
return type Base
with std::remove_cvref_t
:
class Base {
public:
virtual auto DoSomething(const Base&) const -> std::_remove_cvref_t<decltype(*this)> = 0;
};
However, a function that overrides another function must return either the same type as overridden function or a covariant type. Quoting from cppreference:
Two types are covariant if they satisfy all of the following requirements:
- both types are pointers or references (lvalue or rvalue) to classes. Multi-level pointers or references are not allowed.
- the referenced/pointed-to class in the return type of
Base::f()
must be an unambiguous and accessible direct or indirect base class of the referenced/pointed-to class of the return type ofDerived::f()
.- the return type of
Derived::f()
must be equally or less cv-qualified than the return type ofBase::f()
.
So yes, if Base::DoSomething
will return Base
, any overrides must also return Base
. If Base::DoSomething
will return Base&
or Base*
, you can override that with Derived& Derived::DoSomething
(or respectively Derived*
).
What you want to do would be very breaking from compiler point of view. Imagine the following code:
//interface.hpp
struct Base { // sizeof(Base) == 8
virtual Base foo(); // could return through a register
};
//implementation.hpp
struct Derived: public Base { // sizeof(Derived) == 32 at least
std::string x;
Derived foo() override; // too big to return through register, must return through stack
};
//interface_user.cpp
#include "interface.hpp" // doesn't know about implementation.hpp
void bar(Base& myBase) {
Base b = myBase.foo(); // where is it returned? through a register or the stack? compiler knows for sure it should be a register, but...
}
Not to mention the obvious violation of Liskov's subsitution principle.
Upvotes: 3