Reputation: 503
Suppose I want to do partial function application to make a wide range of functions conform to a single signature.
For example, I could want to go from a double-parameter function to a single-parameter function as follows:
std::function<int(int, int)> doubleFoo = [](int i, int j) { return i + j; };
// Turn the function into a single-parameter function, using j = 5
std::function<int(int)> singleFoo = toSingleFoo(doubleFoo, 5);
As I want toSingleFoo
to handle any single- or multi-argument function of which the first argument is an int
, I've defined it as a variadic template function:
template <typename... Args>
std::function<int(int i)> toSingleFoo(std::function<int(int, Args&&...)> multiFoo, Args&&... args)
{
auto singleFoo = [args](int i) { multiFoo(i, std::forward<Args>(args)...) };
return singleFoo;
}
However, that gives the following compiler errors (using Visual Studio 2017, version 15.7.6):
error C2672: 'toSingleFoo': no matching overloaded function found
error C2784: 'std::function<int (int)> toSingleFoo(std::function<int(int,Args &&...)>,Args &&...)':
could not deduce template argument for 'std::function<int(int,Args &&...)>'
from 'std::function<int (int,int)>'
Why is the compiler unable to deduce template arguments, despite an int
being passed as second argument in the example above?
Upvotes: 3
Views: 181
Reputation: 60208
To start off, you need to capture multiFoo
, as well as capture the variadic args...
.
The issue with the deduction seems to be in the std::function
argument. If you only allow it to deduce Args...
from the second argument, deduction proceeds as expected.
To hide the deduction of the first argument, just put it in an identity template
template<typename T>
struct I { using type = T; };
Then you can define the function as
template <typename... Args>
std::function<int(int)> toSingleFoo(
typename I<std::function<int(int, Args&&...)>>::type multiFoo,
Args&&... args)
{
return [multiFoo, &args...] (int i) {
return multiFoo(i, std::forward<Args>(args)...);
};
}
and then use it
int main() {
std::function<int(int, int)> doubleFoo = [](int i, int j) { return i + j; };
// Turn the function in a single-parameter function, using j = 5
std::function<int(int)> singleFoo1 = toSingleFoo(doubleFoo, 5);
std::cout << singleFoo1(3); // prints 8
std::function<int(int, int, int)> tripleFoo = [](int i, int j, int k) { return i * j * k; };
// Turn the function in a single-parameter function, using j = 2, k = 3
std::function<int(int)> singleFoo2 = toSingleFoo(tripleFoo, 2, 3);
std::cout << singleFoo2(4); // prints 24
}
Upvotes: 2