Overload method for unique_ptr and shared_ptr is ambiguous with polymorphism











up vote
23
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
    Nov 26 at 12:49






  • 2




    Nice MCVE, nice question. I'd like to see more of those.
    – YSC
    Nov 26 at 12:52






  • 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
    Nov 26 at 13:19

















up vote
23
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
    Nov 26 at 12:49






  • 2




    Nice MCVE, nice question. I'd like to see more of those.
    – YSC
    Nov 26 at 12:52






  • 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
    Nov 26 at 13:19















up vote
23
down vote

favorite
1









up vote
23
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 Nov 26 at 11:42









Laboratorio Cobotica

1727




1727












  • Converting from std::unique_ptr<Foo> to std::shared_ptr<Scene> is as good as converting to std::unique_ptr<Scene>.
    – felix
    Nov 26 at 12:49






  • 2




    Nice MCVE, nice question. I'd like to see more of those.
    – YSC
    Nov 26 at 12:52






  • 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
    Nov 26 at 13:19




















  • Converting from std::unique_ptr<Foo> to std::shared_ptr<Scene> is as good as converting to std::unique_ptr<Scene>.
    – felix
    Nov 26 at 12:49






  • 2




    Nice MCVE, nice question. I'd like to see more of those.
    – YSC
    Nov 26 at 12:52






  • 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
    Nov 26 at 13:19


















Converting from std::unique_ptr<Foo> to std::shared_ptr<Scene> is as good as converting to std::unique_ptr<Scene>.
– felix
Nov 26 at 12:49




Converting from std::unique_ptr<Foo> to std::shared_ptr<Scene> is as good as converting to std::unique_ptr<Scene>.
– felix
Nov 26 at 12:49




2




2




Nice MCVE, nice question. I'd like to see more of those.
– YSC
Nov 26 at 12:52




Nice MCVE, nice question. I'd like to see more of those.
– YSC
Nov 26 at 12:52




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
Nov 26 at 13:19






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
Nov 26 at 13:19














5 Answers
5






active

oldest

votes

















up vote
18
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
    Nov 26 at 12:55










  • @snake_style clarified. Move constructor was a misnomer
    – Caleth
    Nov 26 at 12:57










  • 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
    Nov 26 at 13:03












  • @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
    Nov 26 at 13:10


















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
      1
      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














        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





















          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
          18
          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
            Nov 26 at 12:55










          • @snake_style clarified. Move constructor was a misnomer
            – Caleth
            Nov 26 at 12:57










          • 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
            Nov 26 at 13:03












          • @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
            Nov 26 at 13:10















          up vote
          18
          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
            Nov 26 at 12:55










          • @snake_style clarified. Move constructor was a misnomer
            – Caleth
            Nov 26 at 12:57










          • 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
            Nov 26 at 13:03












          • @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
            Nov 26 at 13:10













          up vote
          18
          down vote



          accepted







          up vote
          18
          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 Nov 26 at 13:36

























          answered Nov 26 at 11:53









          Caleth

          15.7k22037




          15.7k22037












          • Why it's not ambiguous without polymorphism?
            – snake_style
            Nov 26 at 12:55










          • @snake_style clarified. Move constructor was a misnomer
            – Caleth
            Nov 26 at 12:57










          • 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
            Nov 26 at 13:03












          • @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
            Nov 26 at 13:10


















          • Why it's not ambiguous without polymorphism?
            – snake_style
            Nov 26 at 12:55










          • @snake_style clarified. Move constructor was a misnomer
            – Caleth
            Nov 26 at 12:57










          • 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
            Nov 26 at 13:03












          • @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
            Nov 26 at 13:10
















          Why it's not ambiguous without polymorphism?
          – snake_style
          Nov 26 at 12:55




          Why it's not ambiguous without polymorphism?
          – snake_style
          Nov 26 at 12:55












          @snake_style clarified. Move constructor was a misnomer
          – Caleth
          Nov 26 at 12:57




          @snake_style clarified. Move constructor was a misnomer
          – Caleth
          Nov 26 at 12:57












          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
          Nov 26 at 13:03






          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
          Nov 26 at 13:03














          @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
          Nov 26 at 13:10




          @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
          Nov 26 at 13:10












          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 Nov 26 at 12:34









              jrok

              44.5k685119




              44.5k685119






















                  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 Nov 26 at 13:21









                      Alan Birtles

                      7,810933




                      7,810933






















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










                              up vote
                              1
                              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 Nov 26 at 15:03









                              Handy999

                              1013




                              1013






















                                  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 Nov 26 at 14:25









                                      WaffleSouffle

                                      2,33522223




                                      2,33522223






























                                          draft saved

                                          draft discarded




















































                                          Thanks for contributing an answer to Stack Overflow!


                                          • Please be sure to answer the question. Provide details and share your research!

                                          But avoid



                                          • Asking for help, clarification, or responding to other answers.

                                          • Making statements based on opinion; back them up with references or personal experience.


                                          To learn more, see our tips on writing great answers.





                                          Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


                                          Please pay close attention to the following guidance:


                                          • Please be sure to answer the question. Provide details and share your research!

                                          But avoid



                                          • Asking for help, clarification, or responding to other answers.

                                          • Making statements based on opinion; back them up with references or personal experience.


                                          To learn more, see our tips on writing great answers.




                                          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

                                          How to change which sound is reproduced for terminal bell?

                                          Title Spacing in Bjornstrup Chapter, Removing Chapter Number From Contents

                                          Can I use Tabulator js library in my java Spring + Thymeleaf project?