I am trying to understand the C++ language behaviour that I have observed (I understood the first half of it).
My setup: 2 templated classes: A
and C
. A
can be converted to C
but not the other way around. They have some common behaviour, so I was searching to implement some logic only with C
and rely on the conversion from A
to C
to make A
behave the same way as C
.
The examples uses an operator overloading, but the discussion I think is the same with a function or a method.
What I tried first was to use a conversion constructor:
template <class T>
class A {
};
template <class T>
class C {
public:
C() = default;
C(const A<T>&) {};
};
template <class T>
bool operator<(const C<T> &, const C<T> &) {return true;}
int main() {
A<int> a1, a2;
C<int> c1, c2;
c1 = a1; // OK, no-brainer
c1 < c2; // OK, no-brainer
c1 < a1; // template deduction failure
a1 < c1; // template deduction failure
a1 < a2; // template deduction failure
}
This is actually the first half that I think I understood after searching SO and the net. From what I gathered the template argument must be matched exactly before a conversion can be tried and in this case it can't be deduced without taking into account possible conversions so deduction is not possible. This can be circumvented if the operator is made non-template friend in C
(but I don't like it).
The next thing I tried was to use inheritance:
template <class T>
class C {
};
template <class T>
class A : public C<T> {
};
template <class T>
bool operator<(const C<T> &, const C<T> &) {return true;}
int main() {
A<int> a1, a2;
C<int> c1, c2;
c1 = a1; // Ok, slicing
c1 < c2; // Ok, no-brainer
c1 < a1; // Ok, but why?
a1 < c1; // Ok, but why?
a1 < a2; // Ok, but why?
}
For this I didn't find an explanation on the net (maybe I didn't know how to search).
My question is why can the template be deduced when A
is convertible to C
because is the base class of C
(second case) but cannot be deduced when A
is just convertible to C
(first case)?
Edit
As suggested by KerrekSB in the comments I have tried:
template <class T>
bool operator<(const C<T> &, const typename std::common_type<C<T>>::type &) {return true;}
with my first case (conversion, not inheritance)
In this case:
c1 < c2; // OK
c1 < a1; // OK, a1 is converted
a1 < c1; // template deduction failure
a1 < a2; // template deduction failure
Using his answer I think c1 < a1
works because the second argument is not part of the deduction process so implicit conversion is considered for the second argument.
I also tried:
template <class T>
bool operator<(const typename std::common_type<C<T>>::type &, const typename std::common_type<C<T>>::type &) {return true;}
this doesn't work even with c1 < c2
. I think it's because now no parameter is involved in the deduction process.
Am I right?