如何创建带上下文捕获参数的boost::range::transform重载版本?
解决Boost 1.55 + VS2010中带引用捕获lambda与transform适配器的编译问题
首先,咱们来拆解你遇到的编译失败原因:
在VS2010的实现中,带捕获的lambda(尤其是引用捕获)会被编译器禁用赋值运算符(相当于operator=被delete)。而Boost 1.55的transformed适配器内部会尝试对存储的函数对象执行赋值操作,当它处理带引用捕获的lambda时,就会触发非法赋值构造函数的编译错误。无捕获的lambda可以隐式转换为函数指针,且其赋值构造是合法的,所以能正常运行。
接下来,咱们实现你想要的、类似std::lower_bound的带额外参数的transformed重载,不用写繁琐的完整仿函数:
步骤1:定义二元函数包装器
这个包装器会把你的二元lambda(接收range元素+额外参数)转换成transformed需要的一元函数对象:
#include <boost/range/adaptors.hpp> #include <utility> // 用于std::forward // 如果VS2010没有std::decay,自己实现简化版本 template <typename T> struct decay { typedef typename std::remove_cv<typename std::remove_reference<T>::type>::type type; }; template <typename Func, typename Arg> struct BinaryTransformWrapper { Func func; Arg arg; // 构造函数,完美转发参数 BinaryTransformWrapper(Func&& f, Arg&& a) : func(std::forward<Func>(f)), arg(std::forward<Arg>(a)) {} // 一元调用运算符,把range元素和额外参数传给二元lambda template <typename T> auto operator()(T&& val) -> decltype(func(std::forward<T>(val), arg)) { return func(std::forward<T>(val), arg); } };
步骤2:重载transformed适配器
我们把重载函数放到boost::adaptors命名空间里,这样就能像原生transformed一样用管道语法:
namespace boost { namespace adaptors { template <typename Arg, typename Func> auto transformed(Arg&& arg, Func&& func) -> decltype( transformed( BinaryTransformWrapper< typename decay<Func>::type, typename decay<Arg>::type >(std::forward<Func>(func), std::forward<Arg>(arg)) ) ) { return transformed( BinaryTransformWrapper< typename decay<Func>::type, typename decay<Arg>::type >(std::forward<Func>(func), std::forward<Arg>(arg)) ); } }} // namespace boost::adaptors
步骤3:使用示例
现在你可以按照预期的方式调用,lambda不需要捕获任何变量,完全避免了引用捕获带来的问题:
#include <vector> struct Foo { int x; int y; }; int main() { std::vector<int> source = {1,2,3,4,5}; Foo foo = {10, 20}; // 直接传递Foo对象(会拷贝) auto tr1 = source | boost::adaptors::transformed(foo, [](int i, const Foo& f){ return i + f.x; }); // 如果想传递Foo的引用(避免拷贝),用std::ref包装 auto tr2 = source | boost::adaptors::transformed(std::ref(foo), [](int i, const Foo& f){ return i + f.x; }); // 测试输出 for (auto val : tr1) printf("%d ", val); // 输出:11 12 13 14 15 return 0; }
细节说明
- 我们用
std::forward和decay来处理参数的转发和类型退化,保证无论是左值、右值还是引用都能正确处理。 - 包装器的
operator()用尾置返回类型(auto -> decltype(...))来推导lambda的返回值,兼容VS2010的特性。 - 如果要传递额外参数的引用,记得用
std::ref或std::cref包装,否则会拷贝参数。
内容的提问来源于stack exchange,提问作者bradgonesurfing
相关产品推荐
相关产品推荐

