c++17 compatible std::bind_front alternative
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.