Suppose there is a free operator*
defined somewhere in the namespace ns
:
namespace ns {
struct Dummy {};
template <typename T>
Dummy operator*(T&&) {
return {};
}
}
In a different place there is a base class in the same namespace, that defines member operator*
:
namespace ns {
template <typename T>
struct Base {
T x;
T& operator*() {
return x;
}
};
}
And a lot of types derived from it. They behave identically, but must be distinct, for elsewhere templates need to be specialized on them:
namespace ns {
template <typename T>
struct DerivedA : Base<T> {};
template <typename T>
struct DerivedB : Base<T> {};
template <typename T>
struct DerivedC : Base<T> {};
// etc
}
When I try to use operator*
on a derived class:
ns::DerivedA<int> d;
*d = 42;
GCC yells at me "FOOL! Thou shan't assign int
to Dummy
!", which apparently means that free operator*
is used instead of the member one in the base class.
I have no control at all over free operator, and can't move derived classes to different namespace.
How can I fix this without duplicating operator*
in each derived class?
Short answer, you can do this:
Long answer: in order to figure out what
*d
to call, we have to determine all the viable functions (§13.3.2):There are two:
In order to figure out which one to pick, we have to determine which "implicit conversion sequence" (§13.3.3.1) is better:
which for our first option is an "Exact Match", and for our second overload option is (§13.3.3.1.6):
The ranking of the conversion sequences is (§13.3.3.1.1.3):
I don't know how to insert the table here. But basically we have one "Exact Match" (to call
Dummy operator*(T&&)
) and one "Conversion" (to callT& Base::operator*
), thus the "Exact Match" is the "best viable function". And (§13.3.3.2):That's why the
Dummy operator*(T&& )
is preferred.Now, why does my proposal work? In that case, our two options are:
So we have two "Exact Match" candidates - though one of them is through a template and one of the criteria for choose better viable functions is (§13.3.3.1):
Hence, in this case, we pick the
DerivedA::operator*
. Which is what you want.