How to do function overloading with std::shared_ptr<void> and another type of std::shared_ptr?

  • A+

Try out this following code:

#include <functional> #include <memory>  class C {     public:     void F(std::function<void(std::shared_ptr<void>)>){}     void F(std::function<void(std::shared_ptr<int>)>){} };  int main(){     C c;     c.F([](std::shared_ptr<void>) {}); } 

You'll see a compile error: error: call to member function 'F' is ambiguous     c.F([](std::shared_ptr<void>) {});     ~~^ note: candidate function     void F(std::function<void(std::shared_ptr<void>)>){}          ^ note: candidate function     void F(std::function<void(std::shared_ptr<int>)>){}          ^ 

Is there any way to workaround this ambiguity? Perhaps with SFINAE?


I'm confused but I try an explanation.

I see that your lambda can be accepted by both std::function<void(std::shared_ptr<void>)> and std::function<void(std::shared_ptr<int>)>; you can verify that both the following lines compile

std::function<void(std::shared_ptr<void>)>  f0 = [](std::shared_ptr<void>){}; std::function<void(std::shared_ptr<int>)>   f1 = [](std::shared_ptr<void>){}; 

And this is because (I suppose) a shared pointer to int can be converted to shared pointer to void; you can verify that the following line compile

std::shared_ptr<void> sv = std::shared_ptr<int>{}; 

At this point we can see that calling

c.F([](std::shared_ptr<void>) {}); 

you don't pass a std::function<void(std::shared_ptr<void>)> to F(); you're passing an object that can be converted to both std::function<void(std::shared_ptr<void>)> and std::function<void(std::shared_ptr<int>)>; so an object that can be used to call both versions of F().

So the ambiguity.

Is there any way to workaround this ambiguity? Perhaps with SFINAE?

Maybe with tag dispatching.

You can add an unused argument and a template F()

void F (std::function<void(std::shared_ptr<void>)>, int)  { std::cout << "void version" << std::endl; }  void F (std::function<void(std::shared_ptr<int>)>, long)  { std::cout << "int version" << std::endl; }  template <typename T> void F (T && t)  { F(std::forward<T>(t), 0); } 

This way calling

c.F([](std::shared_ptr<void>) {}); c.F([](std::shared_ptr<int>){}); 

you obtain "void version" from the first call (both non-template F() matches but the "void version" is preferred because 0 is a int) and "int version" from the second call (only the F() "int version" matches).


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