mariob6
mariob6

Reputation: 519

CRTP with abstract interface, dynamic_cast working but dynamic_pointer_cast does not

I'm considering the basic example of CRTP in Wikipedia

#include <memory>

// Base class has a pure virtual function for cloning
class AbstractShape {
 public:
  virtual ~AbstractShape() = default;
  virtual std::unique_ptr<AbstractShape> clone() const = 0;
};

// This CRTP class implements clone() for Derived
template <typename Derived>
class Shape : public AbstractShape {
 public:
  std::unique_ptr<AbstractShape> clone() const override {
    return std::make_unique<Derived>(static_cast<Derived const&>(*this));
  }

 protected:
  // We make clear Shape class needs to be inherited
  Shape() = default;
  Shape(const Shape&) = default;
  Shape(Shape&&) = default;
};

class Square : public Shape<Square> {};

In a main() function, the following compiles

Square sq1;
std::unique_ptr<AbstractShape> sq2 = sq1.clone();
dynamic_cast<Square*>(sq2.get());

But the following does not

Square sq1;
std::unique_ptr<AbstractShape> sq2 = sq1.clone();
std::dynamic_pointer_cast<Square>(sq2.get());

and the compiler gives the error

error: no matching function for call to ‘dynamic_pointer_cast<Square>(std::unique_ptr<AbstractShape>::pointer)’
   std::dynamic_pointer_cast<Square>(sq2.get());
                                              ^
In file included from /usr/include/c++/7/bits/shared_ptr.h:52:0,
                 from /usr/include/c++/7/memory:81,
                 from /home/mario/dev/bayesmix/test/crtp_aux.h:1,
                 from /home/mario/dev/bayesmix/test/crtp.cc:2:
/usr/include/c++/7/bits/shared_ptr_base.h:1571:5: note: candidate: template<class _Tp, class _Tp1, __gnu_cxx::_Lock_policy _Lp> std::__shared_ptr<_Tp1, _Lp> std::dynamic_pointer_cast(const std::__shared_ptr<_Tp2, _Lp>&)
     dynamic_pointer_cast(const __shared_ptr<_Tp1, _Lp>& __r) noexcept
     ^~~~~~~~~~~~~~~~~~~~
/usr/include/c++/7/bits/shared_ptr_base.h:1571:5: note:   template argument deduction/substitution failed:
/home/mario/dev/bayesmix/test/crtp.cc:37:46: note:   mismatched types ‘const std::__shared_ptr<_Tp2, _Lp>’ and ‘std::unique_ptr<AbstractShape>::pointer {aka AbstractShape*}’
   std::dynamic_pointer_cast<Square>(sq2.get());

Basically saying that template deduction failed. However, Square is not templated nor AbstractShape.

Upvotes: 1

Views: 390

Answers (2)

Hanjoung Lee
Hanjoung Lee

Reputation: 2152

You can only pass an shared_ptr as the argument for dynamic_pointer_cast. However you passed a raw pointer. So I guess you have a couple of options.

  1. Just use the first approach (if you do not intend to turn it into a shared_ptr)
  2. Convert sq2 to shared_ptr and pass it (without .get())

Upvotes: 2

Yakk - Adam Nevraumont
Yakk - Adam Nevraumont

Reputation: 275740

std::shared_ptr<AbstractShape> sq2 = sq1.clone();
auto sq3 = std::dynamic_pointer_cast<Square>(sq2);

dynamic pointer cast converts shared ptrs. The template deduction that is failing is that of the cast function.

Upvotes: 4

Related Questions