Overload method for unique_ptr and shared_ptr is ambiguous with polymorphism











up vote
18
down vote

favorite
1












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.










share|improve this question






















  • Converting from std::unique_ptr<Foo> to std::shared_ptr<Scene> is as good as converting to std::unique_ptr<Scene>.
    – felix
    2 days ago






  • 2




    Nice MCVE, nice question. I'd like to see more of those.
    – YSC
    2 days ago






  • 1




    For me if class accepts two different kind of pointers for the same base class is a code smell. This means that something bad happens with memory management inside that class. So to fix it I would remove one of the methods. Probably the unique_ptr version is obsolete since there is easy conversion to shared_ptr. Other way to fix the issue is do not use overload (rename at least one of the methods) what should improve readability.
    – Marek R
    2 days ago

















up vote
18
down vote

favorite
1












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.










share|improve this question






















  • Converting from std::unique_ptr<Foo> to std::shared_ptr<Scene> is as good as converting to std::unique_ptr<Scene>.
    – felix
    2 days ago






  • 2




    Nice MCVE, nice question. I'd like to see more of those.
    – YSC
    2 days ago






  • 1




    For me if class accepts two different kind of pointers for the same base class is a code smell. This means that something bad happens with memory management inside that class. So to fix it I would remove one of the methods. Probably the unique_ptr version is obsolete since there is easy conversion to shared_ptr. Other way to fix the issue is do not use overload (rename at least one of the methods) what should improve readability.
    – Marek R
    2 days ago















up vote
18
down vote

favorite
1









up vote
18
down vote

favorite
1






1





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.










share|improve this question













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.







c++ c++14 overloading shared-ptr unique-ptr






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked 2 days ago









Laboratorio Cobotica

1307




1307












  • Converting from std::unique_ptr<Foo> to std::shared_ptr<Scene> is as good as converting to std::unique_ptr<Scene>.
    – felix
    2 days ago






  • 2




    Nice MCVE, nice question. I'd like to see more of those.
    – YSC
    2 days ago






  • 1




    For me if class accepts two different kind of pointers for the same base class is a code smell. This means that something bad happens with memory management inside that class. So to fix it I would remove one of the methods. Probably the unique_ptr version is obsolete since there is easy conversion to shared_ptr. Other way to fix the issue is do not use overload (rename at least one of the methods) what should improve readability.
    – Marek R
    2 days ago




















  • Converting from std::unique_ptr<Foo> to std::shared_ptr<Scene> is as good as converting to std::unique_ptr<Scene>.
    – felix
    2 days ago






  • 2




    Nice MCVE, nice question. I'd like to see more of those.
    – YSC
    2 days ago






  • 1




    For me if class accepts two different kind of pointers for the same base class is a code smell. This means that something bad happens with memory management inside that class. So to fix it I would remove one of the methods. Probably the unique_ptr version is obsolete since there is easy conversion to shared_ptr. Other way to fix the issue is do not use overload (rename at least one of the methods) what should improve readability.
    – Marek R
    2 days ago


















Converting from std::unique_ptr<Foo> to std::shared_ptr<Scene> is as good as converting to std::unique_ptr<Scene>.
– felix
2 days ago




Converting from std::unique_ptr<Foo> to std::shared_ptr<Scene> is as good as converting to std::unique_ptr<Scene>.
– felix
2 days ago




2




2




Nice MCVE, nice question. I'd like to see more of those.
– YSC
2 days ago




Nice MCVE, nice question. I'd like to see more of those.
– YSC
2 days ago




1




1




For me if class accepts two different kind of pointers for the same base class is a code smell. This means that something bad happens with memory management inside that class. So to fix it I would remove one of the methods. Probably the unique_ptr version is obsolete since there is easy conversion to shared_ptr. Other way to fix the issue is do not use overload (rename at least one of the methods) what should improve readability.
– Marek R
2 days ago






For me if class accepts two different kind of pointers for the same base class is a code smell. This means that something bad happens with memory management inside that class. So to fix it I would remove one of the methods. Probably the unique_ptr version is obsolete since there is easy conversion to shared_ptr. Other way to fix the issue is do not use overload (rename at least one of the methods) what should improve readability.
– Marek R
2 days ago














5 Answers
5






active

oldest

votes

















up vote
14
down vote



accepted










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.






share|improve this answer























  • Why it's not ambiguous without polymorphism?
    – snake_style
    2 days ago










  • @snake_style clarified. Move constructor was a misnomer
    – Caleth
    2 days ago










  • I mean struct D { }; void func(shared_ptr<D> ptr){} void func(unique_ptr<D> ptr){} int main() { auto p = make_unique<D>(); func(std::move(p)); } Why those ctor don't called here in this sample?
    – snake_style
    2 days ago












  • @snake_style in that case, you don't have a template constructor selected for unique_ptr, but the non-template move constructor. Overload resolution prefers non-template conversions
    – Caleth
    2 days ago


















up vote
4
down vote













The other answer explains the ambiguity and a possible solution. Here's another way in case you end up needing both overloads; you can always add another parameter in such cases to break the ambiguity and use tag-dispatching. The boiler-plate code is hidden in private part of Scene:



class Scene
{
struct unique_tag {};
struct shared_tag {};
template<typename T> struct tag_trait;
// Partial specializations are allowed in class scope!
template<typename T, typename D> struct tag_trait<std::unique_ptr<T,D>> { using tag = unique_tag; };
template<typename T> struct tag_trait<std::shared_ptr<T>> { using tag = shared_tag; };

void addObject_internal(std::unique_ptr<Interface> obj, unique_tag);
void addObject_internal(std::shared_ptr<Interface> obj, shared_tag);

public:
template<typename T>
void addObject(T&& obj)
{
addObject_internal(std::forward<T>(obj),
typename tag_trait<std::remove_reference_t<T>>::tag{});
}
};


Full compilable example is here.






share|improve this answer




























    up vote
    3
    down vote













    You have declared two overloads, one taking std::unique_ptr<Interface> and one taking std::shared_ptr<Interface> but are passing in a parameter of type std::unique_ptr<Foo>. As none of your functions match directly the compiler has to perform a conversion to call your function.



    There is one conversion available to std::unique_ptr<Interface> (simple type conversion to unique pointer to base class) and another to std::shared_ptr<Interface> (change to a shared pointer to the base class). These conversions have the same priority so the compiler doesn't know which conversion to use so your functions are ambiguous.



    If you pass std::unique_ptr<Interface> or std::shared_ptr<Interface> there is no conversion required so there is no ambiguity.



    The solution is to simply remove the unique_ptr overload and always convert to shared_ptr. This assumes that the two overloads have the same behaviour, if they don't renaming one of the methods could be more appropriate.






    share|improve this answer




























      up vote
      0
      down vote














      Foos are to be unique_ptrs and Bars are to be shared_ptrs in my main (for reasons explained in the previous question);




      Can you overload in terms of pointer-to-Foo and pointer-to-Bar instead of pointer-to-Interface, since you want to treat them differently ?






      share|improve this answer




























        up vote
        0
        down vote













        The solution by jrok is already quite good. The following approach allows to reuse code even better:



        #include <memory>
        #include <utility>
        #include <iostream>
        #include <type_traits>

        namespace internal {
        template <typename S, typename T>
        struct smart_ptr_rebind_trait {};

        template <typename S, typename T, typename D>
        struct smart_ptr_rebind_trait<S,std::unique_ptr<T,D>> { using rebind_t = std::unique_ptr<S>; };

        template <typename S, typename T>
        struct smart_ptr_rebind_trait<S,std::shared_ptr<T>> { using rebind_t = std::shared_ptr<S>; };

        }

        template <typename S, typename T>
        using rebind_smart_ptr_t = typename internal::smart_ptr_rebind_trait<S,std::remove_reference_t<T>>::rebind_t;

        class Interface
        {
        public:
        virtual ~Interface() = 0;
        };

        inline Interface::~Interface() {}

        class Foo : public Interface {};

        class Bar : public Interface {};

        class Scene
        {
        void addObject_internal(std::unique_ptr<Interface> obj) { std::cout << "uniquen"; }

        void addObject_internal(std::shared_ptr<Interface> obj) { std::cout << "sharedn"; }

        public:

        template<typename T>
        void addObject(T&& obj) {
        using S = rebind_smart_ptr_t<Interface,T>;
        addObject_internal( S(std::forward<T>(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); // ok
        }


        What we do here is to first introduce some helper classes that allow to rebind smart pointers.






        share|improve this answer





















          Your Answer






          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "1"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          convertImagesToLinks: true,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: 10,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














           

          draft saved


          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53480362%2foverload-method-for-unique-ptr-and-shared-ptr-is-ambiguous-with-polymorphism%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          5 Answers
          5






          active

          oldest

          votes








          5 Answers
          5






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes








          up vote
          14
          down vote



          accepted










          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.






          share|improve this answer























          • Why it's not ambiguous without polymorphism?
            – snake_style
            2 days ago










          • @snake_style clarified. Move constructor was a misnomer
            – Caleth
            2 days ago










          • I mean struct D { }; void func(shared_ptr<D> ptr){} void func(unique_ptr<D> ptr){} int main() { auto p = make_unique<D>(); func(std::move(p)); } Why those ctor don't called here in this sample?
            – snake_style
            2 days ago












          • @snake_style in that case, you don't have a template constructor selected for unique_ptr, but the non-template move constructor. Overload resolution prefers non-template conversions
            – Caleth
            2 days ago















          up vote
          14
          down vote



          accepted










          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.






          share|improve this answer























          • Why it's not ambiguous without polymorphism?
            – snake_style
            2 days ago










          • @snake_style clarified. Move constructor was a misnomer
            – Caleth
            2 days ago










          • I mean struct D { }; void func(shared_ptr<D> ptr){} void func(unique_ptr<D> ptr){} int main() { auto p = make_unique<D>(); func(std::move(p)); } Why those ctor don't called here in this sample?
            – snake_style
            2 days ago












          • @snake_style in that case, you don't have a template constructor selected for unique_ptr, but the non-template move constructor. Overload resolution prefers non-template conversions
            – Caleth
            2 days ago













          up vote
          14
          down vote



          accepted







          up vote
          14
          down vote



          accepted






          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.






          share|improve this answer














          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.







          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited 2 days ago

























          answered 2 days ago









          Caleth

          15.3k22037




          15.3k22037












          • Why it's not ambiguous without polymorphism?
            – snake_style
            2 days ago










          • @snake_style clarified. Move constructor was a misnomer
            – Caleth
            2 days ago










          • I mean struct D { }; void func(shared_ptr<D> ptr){} void func(unique_ptr<D> ptr){} int main() { auto p = make_unique<D>(); func(std::move(p)); } Why those ctor don't called here in this sample?
            – snake_style
            2 days ago












          • @snake_style in that case, you don't have a template constructor selected for unique_ptr, but the non-template move constructor. Overload resolution prefers non-template conversions
            – Caleth
            2 days ago


















          • Why it's not ambiguous without polymorphism?
            – snake_style
            2 days ago










          • @snake_style clarified. Move constructor was a misnomer
            – Caleth
            2 days ago










          • I mean struct D { }; void func(shared_ptr<D> ptr){} void func(unique_ptr<D> ptr){} int main() { auto p = make_unique<D>(); func(std::move(p)); } Why those ctor don't called here in this sample?
            – snake_style
            2 days ago












          • @snake_style in that case, you don't have a template constructor selected for unique_ptr, but the non-template move constructor. Overload resolution prefers non-template conversions
            – Caleth
            2 days ago
















          Why it's not ambiguous without polymorphism?
          – snake_style
          2 days ago




          Why it's not ambiguous without polymorphism?
          – snake_style
          2 days ago












          @snake_style clarified. Move constructor was a misnomer
          – Caleth
          2 days ago




          @snake_style clarified. Move constructor was a misnomer
          – Caleth
          2 days ago












          I mean struct D { }; void func(shared_ptr<D> ptr){} void func(unique_ptr<D> ptr){} int main() { auto p = make_unique<D>(); func(std::move(p)); } Why those ctor don't called here in this sample?
          – snake_style
          2 days ago






          I mean struct D { }; void func(shared_ptr<D> ptr){} void func(unique_ptr<D> ptr){} int main() { auto p = make_unique<D>(); func(std::move(p)); } Why those ctor don't called here in this sample?
          – snake_style
          2 days ago














          @snake_style in that case, you don't have a template constructor selected for unique_ptr, but the non-template move constructor. Overload resolution prefers non-template conversions
          – Caleth
          2 days ago




          @snake_style in that case, you don't have a template constructor selected for unique_ptr, but the non-template move constructor. Overload resolution prefers non-template conversions
          – Caleth
          2 days ago












          up vote
          4
          down vote













          The other answer explains the ambiguity and a possible solution. Here's another way in case you end up needing both overloads; you can always add another parameter in such cases to break the ambiguity and use tag-dispatching. The boiler-plate code is hidden in private part of Scene:



          class Scene
          {
          struct unique_tag {};
          struct shared_tag {};
          template<typename T> struct tag_trait;
          // Partial specializations are allowed in class scope!
          template<typename T, typename D> struct tag_trait<std::unique_ptr<T,D>> { using tag = unique_tag; };
          template<typename T> struct tag_trait<std::shared_ptr<T>> { using tag = shared_tag; };

          void addObject_internal(std::unique_ptr<Interface> obj, unique_tag);
          void addObject_internal(std::shared_ptr<Interface> obj, shared_tag);

          public:
          template<typename T>
          void addObject(T&& obj)
          {
          addObject_internal(std::forward<T>(obj),
          typename tag_trait<std::remove_reference_t<T>>::tag{});
          }
          };


          Full compilable example is here.






          share|improve this answer

























            up vote
            4
            down vote













            The other answer explains the ambiguity and a possible solution. Here's another way in case you end up needing both overloads; you can always add another parameter in such cases to break the ambiguity and use tag-dispatching. The boiler-plate code is hidden in private part of Scene:



            class Scene
            {
            struct unique_tag {};
            struct shared_tag {};
            template<typename T> struct tag_trait;
            // Partial specializations are allowed in class scope!
            template<typename T, typename D> struct tag_trait<std::unique_ptr<T,D>> { using tag = unique_tag; };
            template<typename T> struct tag_trait<std::shared_ptr<T>> { using tag = shared_tag; };

            void addObject_internal(std::unique_ptr<Interface> obj, unique_tag);
            void addObject_internal(std::shared_ptr<Interface> obj, shared_tag);

            public:
            template<typename T>
            void addObject(T&& obj)
            {
            addObject_internal(std::forward<T>(obj),
            typename tag_trait<std::remove_reference_t<T>>::tag{});
            }
            };


            Full compilable example is here.






            share|improve this answer























              up vote
              4
              down vote










              up vote
              4
              down vote









              The other answer explains the ambiguity and a possible solution. Here's another way in case you end up needing both overloads; you can always add another parameter in such cases to break the ambiguity and use tag-dispatching. The boiler-plate code is hidden in private part of Scene:



              class Scene
              {
              struct unique_tag {};
              struct shared_tag {};
              template<typename T> struct tag_trait;
              // Partial specializations are allowed in class scope!
              template<typename T, typename D> struct tag_trait<std::unique_ptr<T,D>> { using tag = unique_tag; };
              template<typename T> struct tag_trait<std::shared_ptr<T>> { using tag = shared_tag; };

              void addObject_internal(std::unique_ptr<Interface> obj, unique_tag);
              void addObject_internal(std::shared_ptr<Interface> obj, shared_tag);

              public:
              template<typename T>
              void addObject(T&& obj)
              {
              addObject_internal(std::forward<T>(obj),
              typename tag_trait<std::remove_reference_t<T>>::tag{});
              }
              };


              Full compilable example is here.






              share|improve this answer












              The other answer explains the ambiguity and a possible solution. Here's another way in case you end up needing both overloads; you can always add another parameter in such cases to break the ambiguity and use tag-dispatching. The boiler-plate code is hidden in private part of Scene:



              class Scene
              {
              struct unique_tag {};
              struct shared_tag {};
              template<typename T> struct tag_trait;
              // Partial specializations are allowed in class scope!
              template<typename T, typename D> struct tag_trait<std::unique_ptr<T,D>> { using tag = unique_tag; };
              template<typename T> struct tag_trait<std::shared_ptr<T>> { using tag = shared_tag; };

              void addObject_internal(std::unique_ptr<Interface> obj, unique_tag);
              void addObject_internal(std::shared_ptr<Interface> obj, shared_tag);

              public:
              template<typename T>
              void addObject(T&& obj)
              {
              addObject_internal(std::forward<T>(obj),
              typename tag_trait<std::remove_reference_t<T>>::tag{});
              }
              };


              Full compilable example is here.







              share|improve this answer












              share|improve this answer



              share|improve this answer










              answered 2 days ago









              jrok

              44.5k585119




              44.5k585119






















                  up vote
                  3
                  down vote













                  You have declared two overloads, one taking std::unique_ptr<Interface> and one taking std::shared_ptr<Interface> but are passing in a parameter of type std::unique_ptr<Foo>. As none of your functions match directly the compiler has to perform a conversion to call your function.



                  There is one conversion available to std::unique_ptr<Interface> (simple type conversion to unique pointer to base class) and another to std::shared_ptr<Interface> (change to a shared pointer to the base class). These conversions have the same priority so the compiler doesn't know which conversion to use so your functions are ambiguous.



                  If you pass std::unique_ptr<Interface> or std::shared_ptr<Interface> there is no conversion required so there is no ambiguity.



                  The solution is to simply remove the unique_ptr overload and always convert to shared_ptr. This assumes that the two overloads have the same behaviour, if they don't renaming one of the methods could be more appropriate.






                  share|improve this answer

























                    up vote
                    3
                    down vote













                    You have declared two overloads, one taking std::unique_ptr<Interface> and one taking std::shared_ptr<Interface> but are passing in a parameter of type std::unique_ptr<Foo>. As none of your functions match directly the compiler has to perform a conversion to call your function.



                    There is one conversion available to std::unique_ptr<Interface> (simple type conversion to unique pointer to base class) and another to std::shared_ptr<Interface> (change to a shared pointer to the base class). These conversions have the same priority so the compiler doesn't know which conversion to use so your functions are ambiguous.



                    If you pass std::unique_ptr<Interface> or std::shared_ptr<Interface> there is no conversion required so there is no ambiguity.



                    The solution is to simply remove the unique_ptr overload and always convert to shared_ptr. This assumes that the two overloads have the same behaviour, if they don't renaming one of the methods could be more appropriate.






                    share|improve this answer























                      up vote
                      3
                      down vote










                      up vote
                      3
                      down vote









                      You have declared two overloads, one taking std::unique_ptr<Interface> and one taking std::shared_ptr<Interface> but are passing in a parameter of type std::unique_ptr<Foo>. As none of your functions match directly the compiler has to perform a conversion to call your function.



                      There is one conversion available to std::unique_ptr<Interface> (simple type conversion to unique pointer to base class) and another to std::shared_ptr<Interface> (change to a shared pointer to the base class). These conversions have the same priority so the compiler doesn't know which conversion to use so your functions are ambiguous.



                      If you pass std::unique_ptr<Interface> or std::shared_ptr<Interface> there is no conversion required so there is no ambiguity.



                      The solution is to simply remove the unique_ptr overload and always convert to shared_ptr. This assumes that the two overloads have the same behaviour, if they don't renaming one of the methods could be more appropriate.






                      share|improve this answer












                      You have declared two overloads, one taking std::unique_ptr<Interface> and one taking std::shared_ptr<Interface> but are passing in a parameter of type std::unique_ptr<Foo>. As none of your functions match directly the compiler has to perform a conversion to call your function.



                      There is one conversion available to std::unique_ptr<Interface> (simple type conversion to unique pointer to base class) and another to std::shared_ptr<Interface> (change to a shared pointer to the base class). These conversions have the same priority so the compiler doesn't know which conversion to use so your functions are ambiguous.



                      If you pass std::unique_ptr<Interface> or std::shared_ptr<Interface> there is no conversion required so there is no ambiguity.



                      The solution is to simply remove the unique_ptr overload and always convert to shared_ptr. This assumes that the two overloads have the same behaviour, if they don't renaming one of the methods could be more appropriate.







                      share|improve this answer












                      share|improve this answer



                      share|improve this answer










                      answered 2 days ago









                      Alan Birtles

                      7,635733




                      7,635733






















                          up vote
                          0
                          down vote














                          Foos are to be unique_ptrs and Bars are to be shared_ptrs in my main (for reasons explained in the previous question);




                          Can you overload in terms of pointer-to-Foo and pointer-to-Bar instead of pointer-to-Interface, since you want to treat them differently ?






                          share|improve this answer

























                            up vote
                            0
                            down vote














                            Foos are to be unique_ptrs and Bars are to be shared_ptrs in my main (for reasons explained in the previous question);




                            Can you overload in terms of pointer-to-Foo and pointer-to-Bar instead of pointer-to-Interface, since you want to treat them differently ?






                            share|improve this answer























                              up vote
                              0
                              down vote










                              up vote
                              0
                              down vote










                              Foos are to be unique_ptrs and Bars are to be shared_ptrs in my main (for reasons explained in the previous question);




                              Can you overload in terms of pointer-to-Foo and pointer-to-Bar instead of pointer-to-Interface, since you want to treat them differently ?






                              share|improve this answer













                              Foos are to be unique_ptrs and Bars are to be shared_ptrs in my main (for reasons explained in the previous question);




                              Can you overload in terms of pointer-to-Foo and pointer-to-Bar instead of pointer-to-Interface, since you want to treat them differently ?







                              share|improve this answer












                              share|improve this answer



                              share|improve this answer










                              answered 2 days ago









                              WaffleSouffle

                              2,32522223




                              2,32522223






















                                  up vote
                                  0
                                  down vote













                                  The solution by jrok is already quite good. The following approach allows to reuse code even better:



                                  #include <memory>
                                  #include <utility>
                                  #include <iostream>
                                  #include <type_traits>

                                  namespace internal {
                                  template <typename S, typename T>
                                  struct smart_ptr_rebind_trait {};

                                  template <typename S, typename T, typename D>
                                  struct smart_ptr_rebind_trait<S,std::unique_ptr<T,D>> { using rebind_t = std::unique_ptr<S>; };

                                  template <typename S, typename T>
                                  struct smart_ptr_rebind_trait<S,std::shared_ptr<T>> { using rebind_t = std::shared_ptr<S>; };

                                  }

                                  template <typename S, typename T>
                                  using rebind_smart_ptr_t = typename internal::smart_ptr_rebind_trait<S,std::remove_reference_t<T>>::rebind_t;

                                  class Interface
                                  {
                                  public:
                                  virtual ~Interface() = 0;
                                  };

                                  inline Interface::~Interface() {}

                                  class Foo : public Interface {};

                                  class Bar : public Interface {};

                                  class Scene
                                  {
                                  void addObject_internal(std::unique_ptr<Interface> obj) { std::cout << "uniquen"; }

                                  void addObject_internal(std::shared_ptr<Interface> obj) { std::cout << "sharedn"; }

                                  public:

                                  template<typename T>
                                  void addObject(T&& obj) {
                                  using S = rebind_smart_ptr_t<Interface,T>;
                                  addObject_internal( S(std::forward<T>(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); // ok
                                  }


                                  What we do here is to first introduce some helper classes that allow to rebind smart pointers.






                                  share|improve this answer

























                                    up vote
                                    0
                                    down vote













                                    The solution by jrok is already quite good. The following approach allows to reuse code even better:



                                    #include <memory>
                                    #include <utility>
                                    #include <iostream>
                                    #include <type_traits>

                                    namespace internal {
                                    template <typename S, typename T>
                                    struct smart_ptr_rebind_trait {};

                                    template <typename S, typename T, typename D>
                                    struct smart_ptr_rebind_trait<S,std::unique_ptr<T,D>> { using rebind_t = std::unique_ptr<S>; };

                                    template <typename S, typename T>
                                    struct smart_ptr_rebind_trait<S,std::shared_ptr<T>> { using rebind_t = std::shared_ptr<S>; };

                                    }

                                    template <typename S, typename T>
                                    using rebind_smart_ptr_t = typename internal::smart_ptr_rebind_trait<S,std::remove_reference_t<T>>::rebind_t;

                                    class Interface
                                    {
                                    public:
                                    virtual ~Interface() = 0;
                                    };

                                    inline Interface::~Interface() {}

                                    class Foo : public Interface {};

                                    class Bar : public Interface {};

                                    class Scene
                                    {
                                    void addObject_internal(std::unique_ptr<Interface> obj) { std::cout << "uniquen"; }

                                    void addObject_internal(std::shared_ptr<Interface> obj) { std::cout << "sharedn"; }

                                    public:

                                    template<typename T>
                                    void addObject(T&& obj) {
                                    using S = rebind_smart_ptr_t<Interface,T>;
                                    addObject_internal( S(std::forward<T>(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); // ok
                                    }


                                    What we do here is to first introduce some helper classes that allow to rebind smart pointers.






                                    share|improve this answer























                                      up vote
                                      0
                                      down vote










                                      up vote
                                      0
                                      down vote









                                      The solution by jrok is already quite good. The following approach allows to reuse code even better:



                                      #include <memory>
                                      #include <utility>
                                      #include <iostream>
                                      #include <type_traits>

                                      namespace internal {
                                      template <typename S, typename T>
                                      struct smart_ptr_rebind_trait {};

                                      template <typename S, typename T, typename D>
                                      struct smart_ptr_rebind_trait<S,std::unique_ptr<T,D>> { using rebind_t = std::unique_ptr<S>; };

                                      template <typename S, typename T>
                                      struct smart_ptr_rebind_trait<S,std::shared_ptr<T>> { using rebind_t = std::shared_ptr<S>; };

                                      }

                                      template <typename S, typename T>
                                      using rebind_smart_ptr_t = typename internal::smart_ptr_rebind_trait<S,std::remove_reference_t<T>>::rebind_t;

                                      class Interface
                                      {
                                      public:
                                      virtual ~Interface() = 0;
                                      };

                                      inline Interface::~Interface() {}

                                      class Foo : public Interface {};

                                      class Bar : public Interface {};

                                      class Scene
                                      {
                                      void addObject_internal(std::unique_ptr<Interface> obj) { std::cout << "uniquen"; }

                                      void addObject_internal(std::shared_ptr<Interface> obj) { std::cout << "sharedn"; }

                                      public:

                                      template<typename T>
                                      void addObject(T&& obj) {
                                      using S = rebind_smart_ptr_t<Interface,T>;
                                      addObject_internal( S(std::forward<T>(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); // ok
                                      }


                                      What we do here is to first introduce some helper classes that allow to rebind smart pointers.






                                      share|improve this answer












                                      The solution by jrok is already quite good. The following approach allows to reuse code even better:



                                      #include <memory>
                                      #include <utility>
                                      #include <iostream>
                                      #include <type_traits>

                                      namespace internal {
                                      template <typename S, typename T>
                                      struct smart_ptr_rebind_trait {};

                                      template <typename S, typename T, typename D>
                                      struct smart_ptr_rebind_trait<S,std::unique_ptr<T,D>> { using rebind_t = std::unique_ptr<S>; };

                                      template <typename S, typename T>
                                      struct smart_ptr_rebind_trait<S,std::shared_ptr<T>> { using rebind_t = std::shared_ptr<S>; };

                                      }

                                      template <typename S, typename T>
                                      using rebind_smart_ptr_t = typename internal::smart_ptr_rebind_trait<S,std::remove_reference_t<T>>::rebind_t;

                                      class Interface
                                      {
                                      public:
                                      virtual ~Interface() = 0;
                                      };

                                      inline Interface::~Interface() {}

                                      class Foo : public Interface {};

                                      class Bar : public Interface {};

                                      class Scene
                                      {
                                      void addObject_internal(std::unique_ptr<Interface> obj) { std::cout << "uniquen"; }

                                      void addObject_internal(std::shared_ptr<Interface> obj) { std::cout << "sharedn"; }

                                      public:

                                      template<typename T>
                                      void addObject(T&& obj) {
                                      using S = rebind_smart_ptr_t<Interface,T>;
                                      addObject_internal( S(std::forward<T>(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); // ok
                                      }


                                      What we do here is to first introduce some helper classes that allow to rebind smart pointers.







                                      share|improve this answer












                                      share|improve this answer



                                      share|improve this answer










                                      answered 2 days ago









                                      Handy999

                                      913




                                      913






























                                           

                                          draft saved


                                          draft discarded



















































                                           


                                          draft saved


                                          draft discarded














                                          StackExchange.ready(
                                          function () {
                                          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53480362%2foverload-method-for-unique-ptr-and-shared-ptr-is-ambiguous-with-polymorphism%23new-answer', 'question_page');
                                          }
                                          );

                                          Post as a guest















                                          Required, but never shown





















































                                          Required, but never shown














                                          Required, but never shown












                                          Required, but never shown







                                          Required, but never shown

































                                          Required, but never shown














                                          Required, but never shown












                                          Required, but never shown







                                          Required, but never shown







                                          Popular posts from this blog

                                          If I really need a card on my start hand, how many mulligans make sense? [duplicate]

                                          Alcedinidae

                                          Can an atomic nucleus contain both particles and antiparticles? [duplicate]