C++展开包含lambda类型的参数包时出现奇怪错误该如何解决?
报错原因
这个问题和可变参数模板展开无关,也不是编译器bug,是你的FunctionTypeTraits类型萃取实现存在缺陷,单参数版本能编译通过属于巧合:
- lambda的
operator()是成员函数,你取到的&LambdaType::operator()是成员函数指针类型,而你现有的FunctionTypeTraits特化只匹配了普通自由函数的左值引用,完全没有覆盖成员函数指针的场景。 - 你遇到的错误本质是:主模板把成员函数指针类型传递给了自身的特化版本,但没有匹配的特化实现,导致模板递归推导失败,触发了语法错误。
解决方法
方案1:直接修改foo的签名(最推荐)
如果你的业务逻辑没有强制要求必须接收std::function类型的参数,直接把foo改成接收任意可调用对象即可,完全不需要类型萃取和包装:
template <typename... Functions> void foo(Functions&&... functions) { // 原有业务逻辑可以直接调用functions,不需要额外转换 }
修改后可以直接传入任意数量的lambda,不需要任何包装:
foo([](const std::string & string) {}, [](int & num) {});
方案2:使用C++17的std::function类模板参数推导
如果你不能修改foo的定义,且使用C++17及以上标准,可以直接用std::function的自动推导能力,不用手动写模板参数:
// 自动推导std::function的类型,不需要手动写参数列表 foo(std::function{[](const std::string & string) {}}, std::function{[](int& num) {}});
方案3:修正FunctionTypeTraits的实现
如果需要兼容旧标准且不能修改foo的定义,补全FunctionTypeTraits对成员函数指针的特化即可:
// 主模板 template <typename Function> class FunctionTypeTraits: public FunctionTypeTraits<decltype(&std::remove_reference_t<Function>::operator())> {}; // 特化const成员函数指针(对应普通无修改lambda) template <typename ClassType, typename Param> class FunctionTypeTraits<void (ClassType::*)(Param&) const> { public: using ParameterType = Param; }; // 特化带noexcept的const成员函数指针 template <typename ClassType, typename Param> class FunctionTypeTraits<void (ClassType::*)(Param&) const noexcept> { public: using ParameterType = Param; }; // 特化非const成员函数指针(对应mutable lambda) template <typename ClassType, typename Param> class FunctionTypeTraits<void (ClassType::*)(Param&)> { public: using ParameterType = Param; }; // 特化带noexcept的非const成员函数指针 template <typename ClassType, typename Param> class FunctionTypeTraits<void (ClassType::*)(Param&) noexcept> { public: using ParameterType = Param; }; // 可选:保留原有自由函数的特化,支持传入普通函数 template <typename Param> class FunctionTypeTraits<void (&)(Param&)> { public: using ParameterType = Param; };
修正后你的可变参数fooWrapped就可以正常工作了。
内容的提问来源于stack exchange,提问作者Josef Zoller
相关产品推荐
相关产品推荐

