Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

PrevUpHomeNext

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 closures 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.


PrevUpHomeNext