# c++17 compatible std::bind_front alternative

One of the things that has been really exciting me in c++20 is std::bind_front. Using placeholders with std::bind and boost::bind has really bothered me and the code looked messier and messier with each call to bind. It was bad enough for me to decide to enable -std=c++2a and pray that I wouldn't have to fix the code in the future if something gets changed.

I was messing around with parameter pack recursion when I realized I could make a std::bind_front alternative that would work in c++17. If I removed the std::invoke it would even work in c++11.

It even seems to compile faster than my standard library's implementation.

//#include <utility>
//#include <functional>

//of course this would go into some kind of namespace

template <class F, class A>
struct _bind_obj {
F originalFunc;
A arg;
template <class... Args>
inline auto operator()(Args&&... a){
return std::invoke(originalFunc, arg, std::forward<Args>(a)...);
}
_bind_obj(F &&_originalFunc, A &&_arg) :
originalFunc(std::forward<F>(_originalFunc)),
arg(std::forward<A>(_arg)){

}
};

template <class F, class A>
auto bind_front(F &&func, A &&arg){
return _bind_obj<F, A>(
std::forward<F>(func),
std::forward<A>(arg)
);
}

template <class F, class FirstA, class... A>
auto bind_front(F &&func, FirstA &&firstA, A&&... a){
return bind_front(
bind_front(
std::forward<F>(func),
std::forward<FirstA>(firstA)
),
std::forward<A>(a)...
);
}


So what do you think? Are there some cases in std::bind_front that I missed? Are there any optimizations I should make?

bind_front can (and should) be made constexpr.

The callable object and the bound arguments need to be decayed per the standard.

You can store all arguments in a tuple instead of generating nested wrappers:

template <class FD, class... Args>
class bind_obj {
// ...
FD func;
std::tuple<Args...> args;
};


and then call

std::apply(func, args, std::forward<A>(call_args)...)


(which internally calls invoke.)

Otherwise, nice code.

• Thanks! I see an improvement in compile time and executable size with constexpr. Out of curiosity, what are the advantages of using std::apply and tuples over nested wrappers? Sep 15 '19 at 18:56
• @user233009 You are welcome. With nested wrappers, the compiler has to instantiate the template multiple times (n instantiations of bind_obj for n args) and there are multiple function calls (think of f1(f2(f3(f4(f5, a5), a4), a3), a2), a1) ... scary) whereas std::apply simply expands the template parameter pack, resulting a single function call (f(a1, a2, a3, a4, a5)). That should explain the reduction in both compile time and executable size. Sep 16 '19 at 9:59

Are there some cases in std::bind_front that I missed?

There are several significant differences between std::bind_front's behavior and your implementations.

First, your implementation unconditionally returns a value. But what if the callable in question returned a reference? The behavior is just incorrect.

Second, std::bind_front is SFINAE-friendly but yours is not. That is, I can check to see if I can invoke the result with a certain set of arguments. Here is a silly example demonstrating this:

auto f = [](int i, int j) { return i + j; };
auto g = std::bind_front(f, 1);
auto g2 = your::bind_front(f, 1);

// this is ok, static assertion doesn't trigger
static_assert(!std::is_invocable_v<decltype(g), int, int>);

// this is a compile error
static_assert(!std::is_invocable_v<decltype(g2), int, int>);


Basically, asking the question "Can I call you with two ints?" leads to an instantiation failure outside of the immediate context, and will always be a hard error.

You can fix this and the reference issue described above by specified the correct return type instead of auto:

template <class... Args>
inline std::invoke_result_t<F&, A, Args...> operator()(Args&&... a) { ... }


Third, you're capturing your arguments differently. Consider:

std::string s = "Hello";
auto f = [](std::string const& s, int i) { return s[i]; }

auto g = std::bind_front(f, s);
auto g2 = your::bind_front(f, s);
s = "Goodbye";

assert(g(0) == 'H'); // ok
assert(g2(0) == 'H'); // fails


std::bind_front owns everything. You keep references to all the lvalues. The way the standard library works is if you want to capture by reference, you use std::ref.

Basically, this:

template <class F, class A>
auto bind_front(F &&func, A &&arg){
return _bind_obj<F, A>(
std::forward<F>(func),
std::forward<A>(arg)
);
}


should be:

template <class F, class A>
auto bind_front(F &&func, A &&arg){
return _bind_obj<std::decay_t<F>, std::decay_t<A>>(
std::forward<F>(func),
std::forward<A>(arg)
);
}


Fourth, your result is only invocable on a non-const object. But if the callable I'm bind_front()-ing has a const operator(), I should be able to invoke it as const too right?

The same can be said for ref-qualifiers: if I have a function object with &- and &&-qualified overloads, bind_front should respect that.

The solution is actually to write four overloads of operator():

template <class... Args>
inline std::invoke_result_t<F&, A, Args...> operator()(Args&&... a) &;

template <class... Args>
inline std::invoke_result_t<F const&, A, Args...> operator()(Args&&... a) const&;

template <class... Args>
inline std::invoke_result_t<F, A, Args...> operator()(Args&&... a) &&;

template <class... Args>
inline std::invoke_result_t<F const, A, Args...> operator()(Args&&... a) const &&;


Note that I'm also adjusting the type of F in the type trait, and be sure to move the callable in the rvalue cases.