I have a template that works if I pass it one lambda, but in a related template that takes two lambdas mapped to the same templated type, it cannot deduce that type, and MSVC++ Express 2013 complains the template parameter is ambiguous. To be clear up front, there is no overloading (or specialization) going on here -- my two examples below are the only entities with those identifiers. Here are the templates, which simply apply the callable objects on an argument and return a result:
template <class A, class OP>
auto WhichOp1(A argument, OP firstOp)->decltype(firstOp(argument)) {
return firstOp(argument);
}
template <class A, class OP>
auto WhichOp2(A argument, OP firstOp, OP secondOp)->decltype(firstOp(argument)) {
return firstOp(argument) + secondOp(argument);
}
I can use WhichOp1 successfully like so:
int e = WhichOp1(2, [](int i){return i * 2; });
But a similar call to WhichOp2 won't compile:
int d = WhichOp2(2, [](int i){return i * 2; }, [](int i){return i * 3; });
I get the following errors:
error C2782: 'unknown-type chaj::ops::WhichOp2(A,OP,OP)' : template parameter 'OP' is ambiguous
IntelliSense: no instance of function template "chaj::ops::WhichOp2" matches the argument list argument types are: (int, lambda []int (int i)->int, lambda []int (int i)->int)
What I gather is that it simply can't take the first lambda with the second one and determine between the two what exactly OP's type should be. If I explicitly instantiate, it works fine to resolve the ambiguity:
int b = WhichOp2<int, int(*)(int)>(2, [](int i){return i * 2; }, [](int i){return i * 3; });
So my question is simply an attempt to have a better understanding of what is going on -- why can the compiler not resolve ambiguity when passing two similar lambdas to a template under a common template parameter? The intellisense error seems to map the type of the lambdas to the same type. I won't be surprised if this is compiler specific, but if anyone sees that this works on their compiler, I'd be interested to know.
Each lambda defines a unique type. Even if they take the same parameter(s) and return the same type, two separate lambdas are still two separate types.
Since they're separate types, to the compiler it's roughly as if you had attempted to do something like:
template <class T>
T foo(T a, T b) { return a + b; }
...and then tried to do something like: auto x = foo(1, 2.0);
Since you've passed an int
and a double
, the compiler can't decide whether T
is int
or double
.
The fix is the same in both cases: specify a separate template parameter for each if you might be using lambdas.
Note that this is not unique to lambdas either. The same can happen if you use explicitly defined function objects. For example:
struct foo {
bool operator()();
};
struct bar {
bool operator()();
};
template <class T>
void baz(T a, T b) { /* ... */ }
baz(foo(), bar());
Here, since you've defined foo
and bar
yourself, it's pretty obvious that they're two separate types, even though they both define an operator()
of the same type. Lambda expressions do exactly the same, aside from minor (and irrelevant) details about the names of the classes defined by the lambda expression.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With