...one of the most highly
regarded and expertly designed C++ library projects in the
world.
— Herb Sutter and Andrei
Alexandrescu, C++
Coding Standards
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.