C++20实现通用多函数子组合/管道操作及operator|重载报错解决
报错原因分析
两个方案触发的报错本质是同一个核心问题,外加第一个重载方案的参数定义错误:
- 核心问题:lambda默认的
operator()是const修饰的成员函数,所有按值捕获的对象在该函数内都自带const属性。你定义的F、G的调用运算符没有加const修饰,属于非const成员函数,无法通过const对象调用,因此触发C3848的cv限定符丢失报错。 - 第一个重载方案的额外问题:你定义的lambda参数为
const Inp& arg,其中Inp是第一个函数子F的类型,相当于强制要求调用管道时必须传入F类型的参数,完全不符合传入任意参数调用的预期。
另外注意:要实现g(f(arg))的语义,f的返回值不能为void,否则无法作为参数传递给g,需要先调整你的函数子逻辑,为operator()增加返回值。
正确实现方案
方案1:修改函数子(推荐)
无状态多函数子的调用运算符本身就应该定义为const,修改后可以直接适配所有组合场景:
struct F{ template<typename T> auto operator()(const T& t) const { // 加const修饰,增加返回值 /* 处理逻辑 */ return t; // 示例返回,可根据需求调整 } }; struct G{ template<typename T> auto operator()(const T& t) const { // 加const修饰,增加返回值 /* 处理逻辑 */ return t; // 示例返回,可根据需求调整 } };
通用operator|重载实现,支持链式调用、完美转发:
#include <utility> template<typename Fn1, typename Fn2> auto operator|(Fn1 fn1, Fn2 fn2) { return [fn1 = std::move(fn1), fn2 = std::move(fn2)]<typename T>(T&& arg) -> decltype(auto) { return fn2(fn1(std::forward<T>(arg))); }; }
使用方式和你预期完全一致:
F f; G g; auto pipe = f | g; pipe(123); // 等价于g(f(123)) pipe("text"); // 等价于g(f("text"))
方案2:不修改原函数子
如果你不能修改原有F、G的定义,给lambda加mutable修饰即可,此时lambda的operator()不再是const,可调用捕获对象的非const成员函数:
template<typename Fn1, typename Fn2> auto operator|(Fn1 fn1, Fn2 fn2) { // 加mutable修饰 return [fn1 = std::move(fn1), fn2 = std::move(fn2)]<typename T>(T&& arg) mutable -> decltype(auto) { return fn2(fn1(std::forward<T>(arg))); }; }
如果直接使用lambda组合,修改方式同理:
auto pipe = [=](const auto& arg) mutable { return g(f(arg)); };
内容的提问来源于stack exchange,提问作者Solo
相关产品推荐
相关产品推荐

