Why can't shared_ptr resolve inheritance relationships in function interface?

boost, c++, c++11, templates

Solution

This is not related to `std::shared_ptr<>`. In fact, you could replace that with any class template and get the very same result:

template<typename T> struct X { };

class KBOUM : public X<int> { };

template<typename U>
void do_something(X<K<U>> k) { }

int main()
{
    X<KBOUM> kboom;
    do_something(kboom); // ERROR!

    X<K<int>> k;
    do_something(k); // OK
}

The problem here is that type argument deduction is trying to find a perfect match, and derived-to-base conversions are not attempted.

Only after all template parameters have been unambiguously deduce to produce a perfect match (with the few exceptions allowed by the Standard), possible conversions between arguments are considered during overload resolution.

WORKAROUND:

It is possible to figure out a workaround based on a solution posted by KerrekSB in this Q&A on StackOverflow. First of all, we should define a type trait that allow us to tell whether a certain class is derived from an instance of a certain template:

#include <type_traits>

template <typename T, template <typename> class Tmpl>
struct is_derived
{
    typedef char yes[1];
    typedef char no[2];

    static no & test(...);

    template <typename U>
    static yes & test(Tmpl<U> const &);

    static bool const value = sizeof(test(std::declval<T>())) == sizeof(yes);
};

Then, we could use SFINAE to rewrite `do_something()` as follows (notice that C++11 allows default arguments for function template parameters):

template<class T, std::enable_if<is_derived<T, K>::value>* = nullptr>
void do_something(X<T> k) 
{ 
    // ...
}

With these changes, the program will correctly compile:

int main()
{
    X<KBOUM> kboom;
    do_something(kboom); // OK

    X<K<int>> k;
    do_something(k); // OK
}

And here is a live example.

Problem

Here is a simplified example: ``` #include <memory> #include <vector> template< class T > class K { public: virtual ~K(){} }; class KBOUM : public K<int>{}; template< class U > void do_something( std::shared_ptr< K<U> > k ) { } int main() { auto kboom = std::make_shared<KBOUM>(); do_something( kboom ); // 1 : error std::shared_ptr< K<int> > k = kboom; // 2 : ok do_something( k ); // 3 : ok } ``` With or without boost, whatever the compiler I use I get an error on #1 because `shared_ptr<KBOOM>` don't inherit from `shared_ptr<K<int>>` . However, `KBOOM` does inherit from `K<int>`. You can see that #2 works because shared_ptr is designed to allow implicitly passing a child class pointer to a base class pointer, like raw pointers. So my questions are: - what prevent std::shared_ptr implementer to make it work in case #1 (I mean, assuming that the standard does prevent this case, there should be a reason); - is there a way to write `auto kboom = std::make_shared<KBOUM>(); do_something( kboom );` without looking the int type from K from which KBOOM inherit? Note: I want to avoid the user of the function to have to write ``` std::shared_ptr<K<int>> k = std::make_shared<KBOOM>(); ``` or ``` do_something( std::shared_ptr<K<int>>( kboom ) ); ```

Original source

Related problems