Tutorial: bind_back() and More Complicated View Adaptors

| Home | Libraries | People | FAQ | More

## Tutorial: `bind_back()` and More Complicated View Adaptors

As we saw in the previous section, you can use `boost::stl_interfaces::bind_back()` in combination with `closure` to make closure objects that use the pipe syntax. `std::bind_back()` is an addition to C++23 that is designed to make constructing view adaptors easier.

In some cases, you might have a view adaptor for which `adaptor` is too simple. For instance, let's say you have a view adaptor `foo` that can be parameterized in more than one way. Perhaps `range | foo(a, b)` is a valid use, but so is `range | foo(b, c, d)`. If this is the case, `adaptor`'s all-but-one argument binding is no help.

If you have a view adaptor like this, you should re-introduce an `impl` class like we saw in the `all()` example, and construct `closure`s from `bind_back()` expressions as needed:

    template<typename R>
    struct foo_view
    {
        // etc.
        template<typename R2>
        requires std::is_same_v<std::remove_reference_t<R2>, R>
        foo_view(R2 &&, int, std::string);

        template<typename R2>
        requires std::is_same_v<std::remove_reference_t<R2>, R>
        foo_view(R2 &&, std::string, int, double);
        // etc.
    };

    struct foo_impl
    {
        // These just directly construct the view from the full set of arguments.
        template<typename R>
        auto operator()(R && r, int i, std::string s) const
        {
            return foo_view((R &&)r, i, std::move(s));
        }
        template<typename R>
        auto operator()(R && r, std::string s, int i, double d) const
        {
            return foo_view((R &&)r, std::move(s), i, d);
        }

        // This one binds *this, i, and s into a bind-expression (which is an
        // invocable object), and then uses that invocable to construct a
        // closure.  When the closure is called with a range r, it forwards that
        // range arg to the bind-expression, which calls (*this)(r, i,
        // std::move(s)).
        auto operator()(int i, std::string s) const
        {
            return boost::stl_interfaces::closure(
                boost::stl_interfaces::bind_back(*this, i, std::move(s)));
        }

        // Works just like the overload immediately above, but with a
        // different set of parameters.
        auto operator()(std::string s, int i, double d) const
        {
            return boost::stl_interfaces::closure(
                boost::stl_interfaces::bind_back(*this, std::move(s), i, d));
        }
    };

    inline constexpr foo_impl foo;

With these definitions, you can use `foo` as we discussed earlier; `range | foo(42, "bar")` and `range | foo("bar", 42, 3.14)` are both well-formed, and do what you'd expect.