Various overloads with std :: function parameters are ambiguous with binding (sometimes)

I have two overloads of the foo function that take different std::function , which leads to an ambiguity problem for the latter when used with the result of std::bind . I do not understand why only this is ambiguous.

 void foo(std::function<void(int)>) {} void foo(std::function<int()>) {} void take_int(int) { } int ret_int() { return 0; } 

When using int() with the bind function, I get an ambiguity error

 foo(std::bind(ret_int)); // ERROR 

With gcc-5.1 error (and similar with clang)

 error: call to 'foo' is ambiguous foo(std::bind(ret_int)); ^~~ note: candidate function void foo(std::function<void(int)>) {} ^ note: candidate function void foo(std::function<int()>) {} 

However, all of the following works

 foo(std::bind(take_int, _1)); foo(take_int); foo(ret_int); foo([](){ return ret_int(); }); struct TakeInt { void operator()(int) const { } }; struct RetInt { int operator()() const { return 0; } }; foo(TakeInt{}); foo(RetInt{}); 

Looking at the constructor std::function

 template< class F > function( F f ); 

It would be reasonable to me that any function with several overloads on different types of std::function should have ambiguity, but this is only a problem with calling bind. Then I thought: โ€œMaybe there is some magic to handle function types and lambda, and it does not concern real classesโ€, but it also processes them.

There is a note to en.cppreference here that says [since C ++ 14]

This constructor is not involved in overload resolution unless f is Callable for the argument types Args ... and return type R

+3
source share
1 answer

The problem is how bind is allowed to call. As they say

If some arguments provided in the g () call are not matched by any placeholders stored in g, unused arguments are evaluated and discarded.

In other words, you need to pass at least as many arguments as the underlying called object expects.

This means that the following is valid

 int f(); auto b = std::bind(f); b(1, 2, 3); // arguments aren't used 

So to speak

 auto b = std::bind(ret_int) b(1); 

It works, with 1 discarded, so the following is true, and the choice of overload becomes ambiguous

 std::function<void(int)> f = std::bind(ret_int); 

The converse is not true, however

 std::function<int()> f = std::bind(take_int); 

because take_int cannot be called without arguments.

Takeaway: Lambda> Tie

+4
source

All Articles