能否使用std::bind*实现C++20函数组合?
我正在C++20中尝试函数式编程,已基于Lambda实现了函数组合逻辑,但希望改用std::bind*重构双参数compose函数。我认为std::bind_front的写法比Lambda更简洁,尤其是针对无重载运算符的函数。目前的问题是无法正确传递可变参数给内部函数,尝试过类比单参数场景的写法变体但均失败,想了解可行实现方案或无法实现的原因。
已实现的Lambda版本代码
template <class OuterFn, class InnerFn, class... Args> concept Composable = std::invocable<OuterFn, std::invoke_result_t<InnerFn, Args...>>; template <class OuterFn, class InnerFn> constexpr auto compose(OuterFn&& outer, InnerFn&& inner) { return [ out = std::forward<OuterFn>(outer), in = std::forward<InnerFn>(inner) ]<class... Args>(Args && ... args) requires Composable<OuterFn, InnerFn, Args...> { using std::invoke, std::forward; return invoke(out, invoke(in, forward<Args>(args)...)); }; } template <class OuterFn, class InnerFn> constexpr auto operator*(OuterFn&& outer, InnerFn&& inner) { using std::forward; return compose(forward<OuterFn>(outer), forward<InnerFn>(inner)); } template <class... RestFn> constexpr auto compose(RestFn&&... rest) { return (std::forward<RestFn>(rest) * ...); }
示例对比
- 使用
std::bind_front的写法:
using eq_t = std::equal_to<const std::string_view>; constexpr auto eq_42 = std::bind_front(eq_t{}, "42");
- Lambda写法:
constexpr auto eq_42 = [](const std::string_view sv){ return sv == "42"; };
尝试过的无效写法
std::bind(outer, std::bind_front(inner));
解决方案与分析
为什么直接用std::bind无法实现可变参数的函数组合
std::bind依赖固定的占位符(std::placeholders::_1、std::placeholders::_2等)转发参数,但这些占位符是预定义的固定数量,无法支持任意数量的可变参数Args...。当你写std::bind(outer, std::bind_front(inner))时,内部的bind_front(inner)需要接收参数,但std::bind不会自动将后续传入的所有参数转发给它,除非显式指定占位符——但没法为任意数量的参数生成对应的占位符。
有限参数场景的bind实现(仅作示例)
如果只需要支持固定数量的参数,比如单参数,可以这样写:
template <class OuterFn, class InnerFn> constexpr auto compose_bind(OuterFn&& outer, InnerFn&& inner) { return std::bind( std::forward<OuterFn>(outer), std::bind(std::forward<InnerFn>(inner), std::placeholders::_1) ); }
但这种写法无法扩展到任意数量的可变参数,因为你无法提前知道需要多少个占位符。
结论:Lambda仍是通用函数组合的最优选择
std::bind*系列工具更适合绑定固定数量的参数到函数/函数对象,而通用的函数组合需要处理任意数量的可变参数转发,Lambda的模板参数包Args...可以完美适配这种场景,实现简洁且类型安全的参数转发。
如果一定要用std::bind实现可变参数的函数组合,你需要自己实现一套可变占位符的生成逻辑,这会大幅增加代码复杂度,反而失去了std::bind原本的简洁性,完全不如Lambda版本实用。
内容的提问来源于stack exchange,提问作者hlebyshek

