如何结合Lambda表达式使用boost::binary_traits?
问题描述
我尝试用boost::binary_traits获取仿函数的参数类型,这个方法对std::function有效,但对Lambda类型无效。
代码示例
#include <boost/functional.hpp> template<typename Functor> void foo(Functor&& f) { boost::binary_traits<Functor>::first_argument_type a = 1; // 错误 C2528: 'abstract declarator': pointer to reference is illegal } void bar() { auto func1 = [](int i, int j) { return j; }; std::function<int(int, int)> func2 = [](int i, int j) { return j; }; foo(func1); boost::binary_traits<decltype(func1)>::first_argument_type b = 1; // 错误 C2039: 'result_type': is not a member of 'bar::<lambda_227a80eb4d5d25e23ac8db99e4a9edf0> boost::binary_traits<decltype(func2)>::first_argument_type c = 1; // 正常 boost::binary_traits<std::function<int(int, int)>>::first_argument_type d = 1; // 正常 }
我需要编写能让foo(func1)正常工作的foo函数,背景是要实现如下的accumulate函数,希望头文件里不用添加大量辅助模板让调用者可见:
template<typename Iterable, typename Functor> inline auto accumulate(Iterable& input, Functor&& accumulator) { boost::binary_traits<Functor>::first_argument_type output; std::for_each( std::begin(input), std::end(input), [&output, &accumulator](auto& m) { output = accumulator(output, m); }); return output; }
解决方案
核心原因
boost::binary_traits依赖仿函数类型暴露result_type、first_argument_type等嵌套类型,但C++原生Lambda不会自动生成这些类型;同时foo中直接用转发引用Functor&&实例化boost::binary_traits,会传入引用类型,这也是报错的原因之一。
实用解决方法
可以用C++17及以上的原生类型萃取工具替代Boost的旧特性,兼容Lambda和所有可调用对象,无需额外暴露辅助模板给调用者:
1. 修复foo函数
先实现通用的二元函数类型萃取器,兼容Lambda和std::function:
#include <type_traits> // 通用二元函数类型萃取器 template<typename F, typename = void> struct binary_function_traits {}; // 匹配包含operator()的类型(如Lambda、自定义仿函数) template<typename F> struct binary_function_traits<F, std::void_t<decltype(&F::operator())>> { // 提取operator()的类型 using OpType = decltype(&F::operator()); // 萃取成员函数的返回值和参数类型 template<typename Ret, typename Class, typename Arg1, typename Arg2> struct extractor { using result_type = Ret; using first_argument_type = Arg1; using second_argument_type = Arg2; }; // 适配Lambda的const版本operator() template<typename Ret, typename Class, typename Arg1, typename Arg2> struct extractor<Ret(Class::*)(Arg1, Arg2) const> { using result_type = Ret; using first_argument_type = Arg1; using second_argument_type = Arg2; }; using traits = extractor<OpType>; using result_type = typename traits::result_type; using first_argument_type = typename traits::first_argument_type; using second_argument_type = typename traits::second_argument_type; }; // 适配std::function的特化(可选,通用版已支持) template<typename Ret, typename Arg1, typename Arg2> struct binary_function_traits<std::function<Ret(Arg1, Arg2)>> { using result_type = Ret; using first_argument_type = Arg1; using second_argument_type = Arg2; }; // 便捷别名,自动移除引用和CV限定符 template<typename F> using binary_first_arg = typename binary_function_traits<std::remove_cvref_t<F>>::first_argument_type; // 修复后的foo函数 template<typename Functor> void foo(Functor&& f) { binary_first_arg<Functor> a = 1; }
2. 修复目标accumulate函数
用萃取工具或std::invoke_result推导输出类型,实现更简洁:
#include <type_traits> #include <algorithm> template<typename Iterable, typename Functor> inline auto accumulate(Iterable& input, Functor&& accumulator) { using IterValue = typename std::iterator_traits<decltype(std::begin(input))>::value_type; // 推导accumulator的返回类型,兼容所有可调用对象 using OutputType = std::invoke_result_t<Functor, IterValue, IterValue>; OutputType output{}; // 值初始化 std::for_each( std::begin(input), std::end(input), [&output, &accumulator](auto& m) { output = accumulator(output, m); }); return output; }
关键注意点
- 必须用
std::remove_cvref_t移除转发引用的引用和CV限定符,避免萃取工具处理引用类型 - Lambda的
operator()默认是const的,萃取时要匹配该版本 - C++20可进一步用
std::invocable概念做编译期检查,增强代码健壮性
内容的提问来源于stack exchange,提问作者H.v.M.
相关产品推荐
相关产品推荐

