Частичное связывание аргументов функции

Есть ли способ частично привязать первый / последний N аргументы вызываемого объекта (например, функции) без явного указания остальных аргументов?

std::bind() кажется, требует, чтобы все аргументы должны быть связаны, те, которые должны быть оставлены должны быть связаны с std::placeholders::_1,_2,_3 и т.п.

Можно ли написать bind_first()/bind_last() для частичного связывания, начиная с первого / последнего аргумента и который автоматически вставляет заполнители для любых оставшихся несвязанных аргументов в их исходном порядке в их исходной позиции?

12

Решение

Ни Boost, ни стандартная библиотека bind заполнить пробелы автоматически. Вы могли бы написать такой гаджет самостоятельно, если у вас есть дождливый вечер, чтобы заполнить; Вот пример для отслеживания аргументов только простой функции:

#include <tuple>
#include <type_traits>
#include <utility>

template <typename F, typename ...Args> struct trailing_binder;

template <typename R, typename ...Frgs, typename ...Args>
struct trailing_binder<R(Frgs...), Args...>
{
template <typename ...Brgs>
trailing_binder(R (*f)(Frgs...), Brgs &&... brgs)
: the_function(f)
, the_args(std::forward<Brgs>(brgs)...)
{ }

template <unsigned int ...I> struct intlist {};

template <typename ...Brgs>
typename std::enable_if<sizeof...(Brgs) + sizeof...(Args) == sizeof...(Frgs), R>::type
operator()(Brgs &&... brgs)
{
return unwrap(std::integral_constant<bool, 0 == sizeof...(Args)>(),
intlist<>(),
std::forward<Brgs>(brgs)...);
}

private:
template <unsigned int ...I, typename ...Brgs>
R unwrap(std::false_type, intlist<I...>, Brgs &&... brgs)
{
return unwrap(std::integral_constant<bool, sizeof...(I) + 1 == sizeof...(Args)>(),
intlist<I..., sizeof...(I)>(),
std::forward<Brgs>(brgs)...);
}

template <unsigned int ...I, typename ...Brgs>
R unwrap(std::true_type, intlist<I...>, Brgs &&... brgs)
{
return the_function(std::get<I>(the_args)..., std::forward<Brgs>(brgs)...);
}

R (*the_function)(Frgs...);
std::tuple<Args...> the_args;
};

template <typename R, typename ...Args, typename ...Frgs>
trailing_binder<R(Frgs...), Args...> trailing_bind(R (*f)(Frgs...), Args &&... args)
{
return trailing_binder<R(Frgs...), typename std::decay<Args>::type...>(f, std::forward<Args>(args)...);
}

Использование:

int f(int a, int b, int c, int d) { return a + b + c + d; }

int main()
{
auto b = trailing_bind(f, 1);
return b(3, 8, 13);
}
6

Другие решения

Вдохновленный вопросом, я написал свой собственный препринт с нуля. Хотя в итоге все выглядело очень похоже на всех остальных, но я обещаю, что это оригинально 🙂 — назовите это конвергентной эволюцией

Хотя у него немного другой вкус. Во-первых, он переходит к своему конструктору, но вы можете использовать std::decay (в некотором смысле это имеет смысл, но я не люблю писать std::ref везде). Для другого я добавил поддержку вложенных пребиндов так prebind(foo, prebind(GetRandomNumber))() такой же как prebind(foo)(GetRandomNumber()),

#include <tuple>
#include <type_traits>
using namespace std;

struct pb_tag {}; //use inheritance to mark prebinder structs

//result_of_t will be defined by default in c++1y
template<typename T > using result_of_t = typename result_of<T>::type;
template<typename T> using is_prebinder = is_base_of<pb_tag, typename remove_reference<T>::type >;

//ugly sequence generators for something different
template<int N, int ...S> struct seq : seq<N-1, N, S...> {};
template<int ...S> struct seq<0, S...> {typedef seq type;};

//these three functions are only for nested prebind. they map
//T t -> T t and Prebind<f, T...> -> f(T...)
template<typename T>
auto dispatchee(T&& t, false_type) -> decltype(forward<T>(t)){
return forward<T>(t);
}

template<typename T>
auto dispatchee(T&& t, true_type) -> decltype(t())
{
return t();
}

template<typename T>
auto expand(T&& t) -> decltype(dispatchee(forward<T>(t), is_prebinder<T>()))
{
return dispatchee(forward<T>(t), is_prebinder<T>());
}

template<typename T> using expand_type = decltype(expand(declval<T>()));

//the functor which holds the closure in a tuple
template<typename f, typename ...ltypes>
struct prebinder : public pb_tag
{
tuple<f, ltypes...> closure;
typedef typename seq<sizeof...(ltypes)>::type sequence;
prebinder(f F, ltypes... largs) : closure(F, largs...) {}

template<int ...S, typename ...rtypes>
result_of_t<f(expand_type<ltypes>..., rtypes...)>
apply(seq<0, S...>, rtypes&& ... rargs){
return (get<0>(closure))(expand(get<S>(closure))... , forward<rtypes>(rargs)...);
}

template<typename ...rtypes>
result_of_t<f(expand_type<ltypes>..., rtypes...)>
operator() (rtypes&& ... rargs){
return apply(sequence(), forward<rtypes>(rargs)...);
}
};

template<typename f, typename ...ltypes>
prebinder<f, ltypes...> prebind(f&& F, ltypes&&... largs)
{
return prebinder<f, ltypes...>(forward<f>(F), forward<ltypes>(largs)...);
}

Его также можно легко изменить на postbind.

Использование выглядит так:

int g(int a){ return 1 + a; }

int h(){ return 1; }

int i(int a, int b, int c, int d){
return 1 + a + b + c + d;
}

int main()
{
//completely bound
auto a = prebind(g, 1);
cout << a() << endl;

//nested bind by reference
auto b = prebind(g, a);
cout << b() << endl;
get<1>(a.closure) = 2;
cout << b() << endl;

//bind to prebinder
auto c = prebind(b);
cout << c() << endl;

//nested bind of temp to temp
auto d = prebind(prebind(g), prebind(h));
cout << d() << endl;

//and the one you wanted orginally
auto e = prebind(i, 1, 1, 1);
cout << e(1) << endl;

return 0;
}
0