The following function overloads are ambiguous when passing a lambda. I found out that std::function
can be constructed from most callable types, even if their signature does not match. So the compiler can't tell which function to use.
template <typename T> void each(std::function<void(T)> iterator);
template <typename T> void each(std::function<void(T, id)> iterator);
template <typename T> void each(std::function<void(T&)> iterator);
template <typename T> void each(std::function<void(T&, id)> iterator);
There are some similar questions out here, but none of them could solve my problem. How can I resolve the ambiguity without changing the usage? Morever, at the time I have to explicitly mention the template type. Is there a way around this?
One half of this is LWG issue 2132, removing
std::function
's constructor from overload resolution unless the argument is actually callable for the argument types specified. This requires expression SFINAE support to implement, which VC++ doesn't have.The other half of the issue is overload resolution:
With a library that implements LWG2132, this code prints, perhaps surprisingly:
Why? First, it is possible to construct a
std::function<void(T&, id)>
from[](int, id){}
. After all, the latter can be called with a lvalue of typeint
just fine.Second, in
The second is more specialized than the first by the partial ordering rules for function templates, so it's always chosen by overload resolution.
A possible solution is to extract the signature by manipulating the type of the lambda's
operator ()
:This won't work for generic lambdas (whose
operator()
is a template) or for arbitrary function objects (which may have arbitrarily manyoperator()
overloads).The canonical solution for this is to implement a generic dependency injection point (i.e. single overload) and allow client code to decide what it puts in there. I am unsure how to give an example that makes sense for the code you provided, because I cannot imagine what a function called
each
would do with a parameter called iterator (when that parameter is a functor that returnsvoid
).A function similar to yours would be applied for the visitor pattern, so I will give you an example using that:
Client code:
In this example, it is on the last line (i.e. in client code) that you decide how to plug in a non-matching visitor, not in the interface of the
collection
class.