std::bind_f...::bind_back(3)

πŸ“¦ version 2024.07.05, in nixpkgs unstable, opensuse-leap 16.0

πŸ’– NAME πŸ’–

Okay so bestie, this is the C++ Standard Libary page for std::bind_f...::bind_back(3) β€” and the manual name line is literally std::bind_front,std::bind_back, followed by std::bind_front,std::bind_back again. It’s giving β€œwe said what we said” energy πŸ‘‘.


✨ Synopsis ✨

          Defined in header <functional>
          template< class F, class... Args >                               (1)
       (since C++20)
          constexpr /* unspecified */ bind_front( F&& f, Args&&... args );
          template< auto ConstFn, class... Args >                          (2)
       (since C++26)
          constexpr /* unspecified */ bind_front( Args&&... args );
          template< class F, class... Args >                               (3)
       (since C++23)
          constexpr /* unspecified */ bind_back( F&& f, Args&&... args );
          template< auto ConstFn, class... Args >                          (4)
       (since C++26)
          constexpr /* unspecified */ bind_back( Args&&... args );

Function templates std::bind_front and std::bind_back generate a perfect forwarding call wrapper which allows to invoke the callable target with its (1,2) first or (3,4) last sizeof...(Args) parameters bound to args.

  • 1,3) The call wrapper holds a copy of the target callable object f.
  • 2,4) The call wrapper does not hold a callable target (it is statically determined).
  • 1) std::bind_front(f, bound_args...)(call_args...) is expression-equivalent to std::invoke(f, bound_args..., call_args...).
  • 2) std::bind_front<ConstFn>(bound_args...)(call_args...) is expression-equivalent to std::invoke(ConstFn, bound_args..., call_args...).
  • 3) std::bind_back(f, bound_args...)(call_args...) is expression-equivalent to std::invoke(f, call_args..., bound_args...).
  • 4) std::bind_back<ConstFn>(bound_args...)(call_args...) is expression-equivalent to std::invoke(ConstFn, call_args..., bound_args...).

The following conditions must be true, otherwise the program is ill-formed:

  • (1,3) std::is_constructible_v<std::decay_t<F>, F>,
  • (1,3) std::is_move_constructible_v<std::decay_t<F>>,
  • (2,4) If decltype(ConstFn) is a pointer or a pointer-to-member then ConstFn is not a null pointer,
  • (std::is_constructible_v<std::decay_t<Args>, Args> && ...),
  • (std::is_move_constructible_v<std::decay_t<Args>> && ...).

🌸 Parameters 🌸

Here’s the tea on the parameters, bestie:

  • f β€” Callable object (function object, pointer to function, reference to function, pointer to member function, or pointer to data member) that will be bound to some arguments.
  • args β€” list of the arguments to bind to the (1,2) first or (3,4) last sizeof...(Args) parameters of the callable target.

πŸ¦‹ Type requirements πŸ¦‹

  • std::decay_t<F> must meet the requirements of MoveConstructible.
  • std::decay_t<Args>... must meet the requirements of MoveConstructible.
  • decltype(ConstFn) must meet the requirements of Callable.

⭐ Return value ⭐

A function object (the call wrapper) of type T that is unspecified, except that the types of objects returned by two calls to std::bind_front or std::bind_back with the same arguments are the same.

Let bind-partial be either std::bind_front or std::bind_back.

The returned object has the following properties:

bind-partial return type

🧸 Member objects

The returned object behaves as if it holds:

  • 1,3) A member object fd of type std::decay_t<F> direct-non-list-initialized from std::forward<F>(f), and
  • 1-4) An std::tuple object tup constructed with std::tuple<std::decay_t<Args>...>(std::forward<Args>(args)...), except that the returned object’s assignment behavior is unspecified and the names are for exposition only.

πŸŽ€ Constructors

The return type of bind-partial behaves as if its copy/move constructors perform a memberwise copy/move. It is CopyConstructible if all of its member objects (specified above) are CopyConstructible, and is MoveConstructible otherwise.

🧚 Member function operator()

Given an object G obtained from an earlier call to (1,3) bind-partial(f, args...) or (2,4) bind-partial<ConstFn>(args...), when a glvalue g designating G is invoked in a function call expression g(call_args...), an invocation of the stored object takes place, as if by:

  1. std::invoke(g.fd, std::get<Ns>(g.tup)..., call_args...), when bind-partial is std::bind_front,
  2. std::invoke(ConstFn, std::get<Ns>(g.tup)..., call_args...), when bind-partial is std::bind_front,
  3. std::invoke(g.fd, call_args..., std::get<Ns>(g.tup)...), when bind-partial is std::bind_back,
  4. std::invoke(ConstFn, call_args..., std::get<Ns>(g.tup)...), when bind-partial is std::bind_back,

where

  • Ns is an integer pack 0, 1, ..., (sizeof...(Args) - 1),
  • g is an lvalue in the std::invoke expression if it is an lvalue in the call expression, and is an rvalue otherwise. Thus std::move(g)(call_args...) can move the bound arguments into the call, where g(call_args...) would copy.

The program is ill-formed if g has volatile-qualified type.

The member operator() is noexcept if the std::invoke expression it calls is noexcept (in other words, it preserves the exception specification of the underlying call operator).


πŸ’… Exceptions πŸ’…

  • 1,3) Throw any exception thrown by calling the constructor of the stored function object.
  • 1-4) Throw any exception thrown by calling the constructor of any of the bound arguments.

🌈 Notes 🌈

These function templates are intended to replace std::bind. Unlike std::bind, they do not support arbitrary argument rearrangement and have no special treatment for nested bind-expressions or std::reference_wrappers. On the other hand, they pay attention to the value category of the call wrapper object and propagate exception specification of the underlying call operator.

As described in std::invoke, when invoking a pointer to non-static member function or pointer to non-static data member, the first argument has to be a reference or pointer (including, possibly, smart pointer such as std::shared_ptr and std::unique_ptr) to an object whose member will be accessed.

The arguments to std::bind_front or std::bind_back are copied or moved, and are never passed by reference unless wrapped in std::ref or std::cref.

Typically, binding arguments to a function or a member function using (1) std::bind_front and (3) std::bind_back requires storing a function pointer along with the arguments, even though the language knows precisely which function to call without a need to dereference the pointer. To guarantee β€œzero cost” in those cases, C++26 introduces the versions (2,4) (that accept the callable object as an argument for non-type template parameter).

Feature-test macro vibe check:

  • __cpp_lib_bind_front β€” 201907L β€” (C++20) β€” std::bind_front, (1)
  • __cpp_lib_bind_front β€” 202306L β€” (C++26) β€” Allow passing callable objects as non-type template arguments to std::bind_front, (2)
  • __cpp_lib_bind_back β€” 202202L β€” (C++23) β€” std::bind_back, (3)
  • __cpp_lib_bind_back β€” 202306L β€” (C++26) β€” Allow passing callable objects as non-type template arguments to std::bind_back, (4)

✨ EXAMPLE ✨

okay bestie, this is where the code actually does its little dance πŸ’ƒ β€” hit Run this code, let the asserts vibe-check everything, and watch bind_front / bind_back do their thing. The #if blocks are just version-gating the shiny C++26 extras, iykyk 🌈

       // Run this code

        #include <cassert>
        #include <functional>

        int minus(int a, int b)
        {
            return a - b;
        }

        struct S
        {
            int val;
            int minus(int arg) const noexcept { return val - arg; }
        };

        int main()
        {
            auto fifty_minus = std::bind_front(minus, 50);
            assert(fifty_minus(3) == 47); // equivalent to: minus(50, 3) == 47

            auto member_minus = std::bind_front(&S::minus, S{50});
            assert(member_minus(3) == 47); //: S tmp{50}; tmp.minus(3) == 47

            // Noexcept-specification is preserved:
            static_assert(!noexcept(fifty_minus(3)));
            static_assert(noexcept(member_minus(3)));

            // Binding of a lambda:
            auto plus = [](int a, int b) { return a + b; };
            auto forty_plus = std::bind_front(plus, 40);
            assert(forty_plus(7) == 47); // equivalent to: plus(40, 7) == 47

        #if __cpp_lib_bind_front >= 202306L
            auto fifty_minus_cpp26 = std::bind_front<minus>(50);
            assert(fifty_minus_cpp26(3) == 47);

            auto member_minus_cpp26 = std::bind_front<&S::minus>(S{50});
            assert(member_minus_cpp26(3) == 47);

            auto forty_plus_cpp26 = std::bind_front<plus>(40);
            assert(forty_plus(7) == 47);
        #endif

        #if __cpp_lib_bind_back >= 202202L
            auto madd = [](int a, int b, int c) { return a * b + c; };
            auto mul_plus_seven = std::bind_back(madd, 7);
            assert(mul_plus_seven(4, 10) == 47); //: madd(4, 10, 7) == 47
        #endif

        #if __cpp_lib_bind_back >= 202306L
            auto mul_plus_seven_cpp26 = std::bind_back<madd>(7);
            assert(mul_plus_seven_cpp26(4, 10) == 47);
        #endif
        }

πŸ“š REFERENCES πŸ“š

  • C++26 standard (ISO/IEC 14882:2026):
    • TBD Function templates bind_front and bind_back [func.bind.partial]
  • C++23 standard (ISO/IEC 14882:2023):
    • 22.10.14 Function templates bind_front and bind_back [func.bind.partial]
  • C++20 standard (ISO/IEC 14882:2020):
    • 20.14.14 Function template bind_front [func.bind.front]

πŸ‘€ SEE ALSO πŸ‘€

  • bind β€” binds one or more arguments to a function object. (C++11) (function template)
  • mem_fn β€” creates a function object out of a pointer to a member. (C++11) (function template)

and that’s the whole page, bestie 🫢 the footer’s source/version situation is cppreference.com, and the date on it is 2024.06.10 β€” periodt πŸ’…πŸŽ€

πŸ“¦ where this page comes from