I'm writing a function that returns a partial function, where passing in only some parameters of a function returns a new function with some of the parameters filled in.
I currently have this setup:
template< typename T, typename... Ts>
auto partial(auto func, T t)
{
return [func,t](Ts... stuff) mutable {
return func(t,stuff...);
};
}
This works, but the problem is when t
is passed by value vs by reference.
If t
is a temporary, this is great!
//lambda to add 3 numbers together
auto lambda = [](int x, int y, int z){return x + y + z;};
//supply one parameter, giving us a lambda that adds two numbers to 1
auto lamb = partial<int,int,int>(lambda,1);
//prints 6, great!
std::cout << lamb(2,3) << "\n";
Thing is, what if I want to pass a large parameter that is by reference?
std::vector<int> nums;
//lambda is supposed to add an int to a vector
auto lambda = [](std::vector<int>& vec, int y){vec.push_back(y);};
//return partial function that is supposed add an int to "nums"
//instead, it returns a partial function that adds an int to a copy of "nums"
auto lamb = partial<std::vector<int>&,int>(lambda,nums);
lamb(100); //should add 100 to my vector but doesn't
std::cout << nums[0] << "\n"; //definitely not 100
Even though I specified std::vector<int>&
as my type, the lambda still copies the vector as opposed to copying the reference. I'm aware that I can capture by reference in the lambda, but that causes the previous example where I want to copy the primitive to fail.
So yeah, is there a workaround for this? I thought about writing an almost identical partial
function that specifically only deals with references but having a function for values and one for references that both do similar things seems kind of cringe.
You could split your function into two depending on whether T
is a reference type or not:
template <typename T, typename Func>
requires (!std::is_reference_v<T>)
auto partial(Func&& func, T t)
{
return [func=std::forward<Func>(func),t](auto&&... stuff) mutable {
return func(t, std::forward<decltype(stuff)>(stuff)...);
};
}
template <typename T, typename Func>
requires std::is_reference_v<T>
auto partial(Func&& func, T&& t)
{
return [func=std::forward<Func>(func),&t=std::forward<T>(t)](auto&&... stuff) mutable {
return func(std::forward<T>(t), std::forward<decltype(stuff)>(stuff)...);
};
}
I wouldn't though. As mentioned in the comments, the standard library already has std::bind_front
which works
almost exactly like your existing partial
function. You can combine it with std::reference_wrapper
to get the behavior you want:
#include <iostream>
#include <vector>
#include <functional>
int main() {
std::vector<int> nums;
//lambda is supposed to add an int to a vector
auto lambda = [](std::vector<int>& vec, int y){vec.push_back(y);};
auto lamb = std::bind_front(lambda, std::ref(nums));
lamb(100);
std::cout << nums[0] << "\n";
}