Template deduction: why it works with inheritance

2019-08-03 05:48发布

问题:

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?

回答1:

I think your situation is described by C++11, 14.8.2.1/4 [temp.deduct.call]:

In general, the deduction process attempts to find template argument values that will make the deduced A identical to A (after the type A is transformed as described above). However, there are three cases that allow a difference:

— ...

— If P is a class and P has the form simple-template-id, then the transformed A can be a derived class of the deduced A. Likewise, if P is a pointer to a class of the form simple-template-id, the transformed A can be a pointer to a derived class pointed to by the deduced A.

[Note: as specified in 14.8.1, implicit conversions will be performed on a function argument to convert it to the type of the corresponding function parameter if the parameter contains no template-parameters that participate in template argument deduction. Such conversions are also allowed, in addition to the ones described in the preceding list. — end note]

The listed clause says that derived types are OK (note that C<T> is a simple-template-id, by 14.2/1), and the note explains that implicit conversions are only considered for types that aren't themselves part of the deduction process.