Overload method for unique_ptr and shared_ptr is ambiguous with polymorphism











up vote
14
down vote

favorite












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
    13 hours ago






  • 2




    Nice MCVE, nice question. I'd like to see more of those.
    – YSC
    13 hours 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
    12 hours ago

















up vote
14
down vote

favorite












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
    13 hours ago






  • 2




    Nice MCVE, nice question. I'd like to see more of those.
    – YSC
    13 hours 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
    12 hours ago















up vote
14
down vote

favorite









up vote
14
down vote

favorite











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 14 hours ago









Laboratorio Cobotica

1107




1107












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






  • 2




    Nice MCVE, nice question. I'd like to see more of those.
    – YSC
    13 hours 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
    12 hours ago




















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






  • 2




    Nice MCVE, nice question. I'd like to see more of those.
    – YSC
    13 hours 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
    12 hours ago


















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




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




2




2




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




Nice MCVE, nice question. I'd like to see more of those.
– YSC
13 hours 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
12 hours 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
12 hours ago














5 Answers
5






active

oldest

votes

















up vote
12
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
    13 hours ago










  • @snake_style clarified. Move constructor was a misnomer
    – Caleth
    13 hours 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
    13 hours 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
    12 hours ago


















up vote
3
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
    2
    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
          12
          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
            13 hours ago










          • @snake_style clarified. Move constructor was a misnomer
            – Caleth
            13 hours 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
            13 hours 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
            12 hours ago















          up vote
          12
          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
            13 hours ago










          • @snake_style clarified. Move constructor was a misnomer
            – Caleth
            13 hours 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
            13 hours 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
            12 hours ago













          up vote
          12
          down vote



          accepted







          up vote
          12
          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 12 hours ago

























          answered 14 hours ago









          Caleth

          15.1k21937




          15.1k21937












          • Why it's not ambiguous without polymorphism?
            – snake_style
            13 hours ago










          • @snake_style clarified. Move constructor was a misnomer
            – Caleth
            13 hours 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
            13 hours 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
            12 hours ago


















          • Why it's not ambiguous without polymorphism?
            – snake_style
            13 hours ago










          • @snake_style clarified. Move constructor was a misnomer
            – Caleth
            13 hours 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
            13 hours 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
            12 hours ago
















          Why it's not ambiguous without polymorphism?
          – snake_style
          13 hours ago




          Why it's not ambiguous without polymorphism?
          – snake_style
          13 hours ago












          @snake_style clarified. Move constructor was a misnomer
          – Caleth
          13 hours ago




          @snake_style clarified. Move constructor was a misnomer
          – Caleth
          13 hours 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
          13 hours 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
          13 hours 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
          12 hours 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
          12 hours ago












          up vote
          3
          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













            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










              up vote
              3
              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 13 hours ago









              jrok

              44.4k584118




              44.4k584118






















                  up vote
                  2
                  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
                    2
                    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
                      2
                      down vote










                      up vote
                      2
                      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 12 hours ago









                      Alan Birtles

                      7,525733




                      7,525733






















                          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 11 hours ago









                              WaffleSouffle

                              2,30522223




                              2,30522223






















                                  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 11 hours ago









                                      Handy999

                                      912




                                      912






























                                           

                                          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

                                          Entries order in /etc/network/interfaces

                                          新発田市

                                          Grub takes very long (several minutes) to open Menu (in Multi-Boot-System)