c++inheritanceconstructorsmart-pointers

How to return smart pointers and covariance in C++


I am following this tutorial to understand how to return smart pointers and covariance in C++.

  #include <memory>
  #include <iostream>

  class cloneable
  {
  public:
     virtual ~cloneable() {}

     std::unique_ptr<cloneable> clone() const
     {
        return std::unique_ptr<cloneable>(this->clone_impl());
     }

  private:
     virtual cloneable * clone_impl() const = 0;
  };
   
  ///////////////////////////////////////////////////////////////////////////////

  template <typename Derived, typename Base>
  class clone_inherit: public Base
  {
  public:
     std::unique_ptr<Derived> clone() const
     {
        return std::unique_ptr<Derived>(static_cast<Derived *>(this->clone_impl()));
     }

  private:
     virtual clone_inherit * clone_impl() const override
     {
        return new Derived(*this); // getting error here 
     }
  };


  class concrete : public clone_inherit<concrete, cloneable>
  {
    
  };

  int main()
  {
    std::unique_ptr<concrete> c = std::make_unique<concrete>();
  }

When I execute this example I get the following error:

  /tmp/0RmVdQYjfA.cpp: In instantiation of 'clone_inherit<Derived, Base>* clone_inherit<Derived, Base>::clone_impl() const [with Derived = concrete; Base = cloneable]':
  /tmp/0RmVdQYjfA.cpp:30:28:   required from here
  /tmp/0RmVdQYjfA.cpp:32:14: error: no matching function for call to 'concrete::concrete(const clone_inherit<concrete, cloneable>&)'
     32 |       return new Derived(*this);
        |              ^~~~~~~~~~~~~~~~~~
  /tmp/0RmVdQYjfA.cpp:37:7: note: candidate: 'constexpr concrete::concrete()'
     37 | class concrete : public clone_inherit<concrete, cloneable>
        |       ^~~~~~~~
  /tmp/0RmVdQYjfA.cpp:37:7: note:   candidate expects 0 arguments, 1 provided
  /tmp/0RmVdQYjfA.cpp:37:7: note: candidate: 'constexpr concrete::concrete(const concrete&)'
  /tmp/0RmVdQYjfA.cpp:37:7: note:   no known conversion for argument 1 from 'const clone_inherit<concrete, cloneable>' to 'const concrete&'
  /tmp/0RmVdQYjfA.cpp:37:7: note: candidate: 'constexpr concrete::concrete(concrete&&)'
  /tmp/0RmVdQYjfA.cpp:37:7: note:   no known conversion for argument 1 from 'const clone_inherit<concrete, cloneable>' to 'concrete&&'

To fix this error in line 32, I had to return exactly the return pointer to a Derived class as follows:

  //   return  new static_cast<Derived*>(*this); to replace with 
  return new Derived(static_cast<const Derived&>(*this));

Can someone suggest the proper way to fix this?


Solution

  • You can add a private implicit or explicit cast operator:

    template <typename Derived, typename Base>
    class clone_inherit : public Base {
     public:
      std::unique_ptr<Derived> clone() const {
        return std::unique_ptr<Derived>(static_cast<Derived *>(this->clone_impl()));
      }
    
     private:
      // added here
      explicit operator const Derived &() const {
        return static_cast<const Derived &>(*this);
      }
    
      virtual clone_inherit *clone_impl() const override {
        return new Derived(*this);  // not getting error here
      }
    };