In the following example, type inference fails for the call to mkPair2
:
#include <functional>
template <class A, class B>
struct Pair {
A left; B right;
};
template <class A, class B>
Pair<A,B> mkPair1 (A left, B right) {
return (Pair<A,B>) { left, right };
}
template <class A, class B>
std::function<Pair<A,B>(B)> mkPair2 (A left) {
return [left] (B right) {
return (Pair<A,B>) { left, right };
};
}
Pair<int, char> ex1 = mkPair1 (2, 'a');
Pair<int, char> ex2 = mkPair2 (2) ('a');
The problem is that mkPair2
has two template arguments, but the call (2)
only provides it with one of them, so the compiler immediately throws its hands up and decides that the program is ambiguous, even though the second type can be inferred from the following ('a')
call.
This can be resolved by giving the compiler the types manually mkPair2<int,char> (2) ('a')
, but having to hold the compilers hand like that gets old really quickly.
Is there any way to trick the compiler into continuing type checking, provided that every type will eventually be resolved?
even though the second type can be inferred from the following ('a') call.
Yes, it could be inferred but the rules of C++ don't allow for that. Type deduction only happens with the function parameters. So if you are lacking a function parameter for the template parameter then you need to specify it yourself.
That said, C++14's auto return type deduction and generic lambdas will save you from having to specify anything. You can rewrite the code as
template <class A, class B>
struct Pair {
A left; B right;
};
template <class A, class B>
auto mkPair1 (A left, B right) {
return Pair<A,B>{ left, right };
}
template <class A>
auto mkPair2 (A left) {
return [left] (auto right) {
return Pair<A, decltype(right)>{ left, right };
};
}
Pair<int, char> ex1 = mkPair1 (2, 'a');
Pair<int, char> ex2 = mkPair2 (2) ('a');
and everything will be deduced for you. It also returns the lambda object, instead of std::function
so you avoid the cost of the type erasure that std::function
uses.
In addition to @NathanOliver's answer, you can make it work for C++11 too by creating the equivalent of the generic lambda yourself:
template <class A>
struct X {
A left;
X(A left) : left(left) {}
template <class B>
Pair<A,B> operator()(B right) {
return Pair<A,B>{left, right};
}
};
Then:
template <class A>
X<A> mkPair2(A left) {
return X<A>(left);
}
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