Overload method for unique_ptr and shared_ptr is ambiguous with polymorphism

  • A+
Category:Languages

Coding stuff after taking the hint from my previous question's answer, I ran into an issue with overloading Scene::addObject.

To reiterate the relevant bits and make this self contained, with the least details possible:

  • I have a hierarchy of objects inheriting from Interface of which there are Foos and Bars;
  • I have a Scene which owns these objects;
  • Foos are to be unique_ptrs and Bars are to be shared_ptrs in my main (for reasons explained in the previous question);
  • the main passes them to the Scene instance, which takes ownership.

Minimal code example is this:

#include <memory> #include <utility>  class Interface { public:   virtual ~Interface() = 0; };  inline Interface::~Interface() {}  class Foo : public Interface { };  class Bar : public Interface { };  class Scene { public:   void addObject(std::unique_ptr<Interface> obj); //  void addObject(std::shared_ptr<Interface> obj); };  void Scene::addObject(std::unique_ptr<Interface> obj) { }  //void Scene::addObject(std::shared_ptr<Interface> obj) //{ //}  int main(int argc, char** argv) {   auto scn = std::make_unique<Scene>();    auto foo = std::make_unique<Foo>();   scn->addObject(std::move(foo));  //  auto bar = std::make_shared<Bar>(); //  scn->addObject(bar); } 

Uncommenting the commented lines results in:

error: call of overloaded 'addObject(std::remove_reference<std::unique_ptr<Foo, std::default_delete<Foo> >&>::type)' is ambiguous     scn->addObject(std::move(foo));                                  ^  main.cpp:27:6: note: candidate: 'void Scene::addObject(std::unique_ptr<Interface>)'   void Scene::addObject(std::unique_ptr<Interface> obj)        ^~~~~  main.cpp:31:6: note: candidate: 'void Scene::addObject(std::shared_ptr<Interface>)'   void Scene::addObject(std::shared_ptr<Interface> obj)        ^~~~~ 

Uncommenting the shared and commenting the unique stuff also compiles, so I take it the problem is, like the compiler says, in the overload. However I need the overload as both these types will need to be stored in some kind of collection, and they are indeed kept as pointers to base (possibly all moved into shared_ptrs).

I'm passing both by-value because I want to make clear I'm taking ownership in Scene (and upping the reference counter for the shared_ptrs). Not really clear to me where the issue lies at all, and I couldn't find any example of this elsewhere.

 


The problem you are encountering is this constructor of shared_ptr (13), (which is not explicit), is as good a match as a similar "moving derived to base" constructor of unique_ptr (6) (also not explicit).

template< class Y, class Deleter >  shared_ptr( std::unique_ptr<Y,Deleter>&& r ); // (13) 

13) Constructs a shared_ptr which manages the object currently managed by r. The deleter associated with r is stored for future deletion of the managed object. r manages no object after the call.

This overload doesn't participate in overload resolution if std::unique_ptr<Y, Deleter>::pointer is not compatible with T*. If r.get() is a null pointer, this overload is equivalent to the default constructor (1). (since C++17)

template< class U, class E > unique_ptr( unique_ptr<U, E>&& u ) noexcept; //(6) 

6) Constructs a unique_ptr by transferring ownership from u to *this, where u is constructed with a specified deleter (E).

This constructor only participates in overload resolution if all of the following is true:

a) unique_ptr<U, E>::pointer is implicitly convertible to pointer

b) U is not an array type

c) Either Deleter is a reference type and E is the same type as D, or Deleter is not a reference type and E is implicitly convertible to D

In the non polymorphic case, you are constructing a unique_ptr<T> from a unique_ptr<T>&&, which uses the non-template move constructor. There overload resolution prefers the non-template


I'm going to assume that Scene stores shared_ptr<Interface>s. In that case you don't need to overload addObject for unique_ptr, you can just allow the implicit conversion in the call.

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: