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 &lt;utility&gt;
//#include &lt;functional&gt;

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

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

    }
};

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

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




So what do you think? Are there some cases in std::bind_front that I missed? Are there any optimizations I should make?
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&lt;decltype(g), int, int&gt;);

// this is a compile error
static_assert(!std::is_invocable_v&lt;decltype(g2), int, int&gt;);


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 &lt;class... Args&gt;
inline std::invoke_result_t&lt;F&amp;, A, Args...&gt; operator()(Args&amp;&amp;... a) { ... }




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

std::string s = "Hello";
auto f = [](std::string const&amp; 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 &lt;class F, class A&gt;
auto bind_front(F &amp;&amp;func, A &amp;&amp;arg){
    return _bind_obj&lt;F, A&gt;(
        std::forward&lt;F&gt;(func),
        std::forward&lt;A&gt;(arg)
    );
}


should be:

template &lt;class F, class A&gt;
auto bind_front(F &amp;&amp;func, A &amp;&amp;arg){
    return _bind_obj&lt;std::decay_t&lt;F&gt;, std::decay_t&lt;A&gt;&gt;(
        std::forward&lt;F&gt;(func),
        std::forward&lt;A&gt;(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 &amp;- and &amp;&amp;-qualified overloads, bind_front should respect that.

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

template &lt;class... Args&gt;
inline std::invoke_result_t&lt;F&amp;, A, Args...&gt; operator()(Args&amp;&amp;... a) &amp;;

template &lt;class... Args&gt;
inline std::invoke_result_t&lt;F const&amp;, A, Args...&gt; operator()(Args&amp;&amp;... a) const&amp;;

template &lt;class... Args&gt;
inline std::invoke_result_t&lt;F, A, Args...&gt; operator()(Args&amp;&amp;... a) &amp;&amp;;

template &lt;class... Args&gt;
inline std::invoke_result_t&lt;F const, A, Args...&gt; operator()(Args&amp;&amp;... a) const &amp;&amp;;


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.