Search code examples
c++smart-pointersimplicit-conversion

How to write constructors for implicit conversions from instances of a templated class C<Derived> to instances of C<Base>


I have created a template class derived from shared_ptr<const T> that enables a holder of the shared pointer to modify the pointed instance by cloning the instance and reseting the shared_pointer. (T can be a clonable class or a class that have a copy constructor whence the weird double definition of the modify method)

shared_ptr<Base> can be implicitly constructed/copied by a instance of class shared_ptr<Derived>. But my class cannot.

How can I make that possible? I suppose I need to create a templated constructor that accepts argument of type const_shared_ptr<D> where D is a type derived from the template type C. I don't know how to write that. I guess it is possible as shared_ptr can do it.

template <typename T, typename = int>
struct HasPolymorphicClonage : std::false_type { };
template <typename T>
struct HasPolymorphicClonage <T, decltype(&T::clone, 0)> : std::true_type { };

template<typename C>
class shared_const_ptr : public std::shared_ptr<const C>
{
public :
    shared_const_ptr(const C * c) : std::shared_ptr<const C>(c) {}
    shared_const_ptr() =default;

    // définition de la méthode modify selon que T propose ou non une méthode clone
    template<typename T1 = C>
    typename std::enable_if_t<HasPolymorphicClonage<T1>::value,C&> modify()
    {
        C * remplacant = (*this)->clone();
        this->std::shared_ptr<const C>::reset(remplacant);
        return *remplacant;
    }

    template<typename T1 = C>
    typename std::enable_if_t<!HasPolymorphicClonage<T1>::value,C&> modify()
    {
        C * remplacant = new C(**this); // on utilise le constructeur par recopie
        this->std::shared_ptr<const C>::reset(remplacant);
        return *remplacant;
    }

};

Solution

  • Adding a templated constructor accepting a std::shared_ptr<T> should work:

    template<typename C>
    class shared_const_ptr : public std::shared_ptr<const C>
    {
    public :
        // ...
    
        template <typename T>
        shared_const_ptr(std::shared_ptr<const T> const& other) : std::shared_ptr<const C>(other) {}
    
    };
    
    struct Base {};
    struct Derived : Base {};
    
    int main()
    {
        shared_const_ptr<Base> b;
        shared_const_ptr d(new Derived{});
        b = d;
    }
    

    https://godbolt.org/z/n45PKE9ff

    When constructing b fromd, argument conversion will convert d to a std::shared_ptr<const Derived>. The constructor is viable because std::shared_ptr<const Derived> is convertible to std::shared_ptr<const Base>.