Reputation: 199
Compiling the following contrived example:
class Base
{};
class Derived : public Base
{};
template< typename T >
class A
{};
class B
{
public:
static void f( const A< Base >& ) {}
};
int main()
{
A< Base > tb;
A< Derived > td;
B::f( tb );
B::f( td );
return 0;
}
using g++-8 gives me the following error:
error: no matching function for call to 'B::f(A<Derived>&)'
B::f( td );
note: no known conversion for argument 1 from 'A<Derived>' to 'const A<Base>&'
Why?
Since Derived
is-a Base
and it doesn't override any of Base
's stuff, why can't I give a Derived
in the place of a Base
in the templated function parameter?
Upvotes: 1
Views: 539
Reputation: 13269
Template instances, like A<Base>
and A<Derived>
, are different types. In particular they do not have any inheritance relationship even if Base
and Derived
do.
There are quite a few ways you can make what you want work.
First, you could make A<Derived>
explicitly derive from A<Base>
, but that means adding a whole class definition.
template<>
class A<Derived> : public A<Base>
{};
Second, you can provide an implicit conversion from A<Derived>
to A<Base>
in the form of a constructor template. You can use std::enable_if
and std::is_base_of
to only allow A<T>
where T
is derived from Base
, or directly std::is_same
if you only want to consider this particular Derived
type.
template<typename T>
class A
{
template<typename U, typename = std::enable_if_t<std::is_base_of_v<T, U>>>
A(A<U> const& other);
};
Third, you can provide an implicit conversion in the form of an operator template, in much the same way.
template<typename T>
class A
{
template<typename U, typename = std::enable_if_t<std::is_base_of_v<U, T>>>
operator U();
};
Fourth, you can make f
a function template and restrict what types it takes.
template<typename T, typename = std::enable_if_t<std::is_base_of_v<Base, T>>>
static void f(A<T> const& a);
Upvotes: 1
Reputation: 118300
It is true that Derived
is derived from Base
, but that doesn't mean that A<Derived>
must therefore be derived from A<Base>
. C++ templates don't work this way.
All that A<Derived>
is, is a class, instantiated by the A
template. You could've simply declared:
class A_Derived {
// ...
};
With the same members (if it had any), and pretty much got the same results. Same for A<Base>
. With nothing else in the picture, the two classes have absolutely nothing to do with each other, whatsoever. You can draw a mental picture here, as if you made the following declarations:
class A_Derived {
};
and
class A_Base {
};
Which is pretty much what this is history. Do you see A_Derived
being explicitly derived from A_Base
here? Obviously not. If something expects a reference or a pointer to A_Base
, you cannot give it A_Derived
, because the two classes have absolutely nothing to do with each other. They are independent classes.
P.S. You could declare an explicit specialization of A<Derived>
as being derived from A<Base>
, if you so wish, but specialization is a completely different topic...
Upvotes: 3