为何std::async无法匹配重载成员函数而lambda可正常调用?
问题说明
注意:本问题不询问如何让下方代码片段正常运行,相关解决方案已有公开讨论。核心疑问为:为何
std::async本身无法找到匹配的重载函数,但lambda表达式却可以?编译器报错提示不存在如下形式的匹配调用:
'async(std::launch, <unresolved overloaded function type>, std::shared_ptr<Demo>, int)'
传入lambda(即[ptr=shared_from_this()](){return ptr->foo(2);)时,编译器可正确找到合适的重载版本,该差异的原因是什么?
复现代码
#include <future> #include <functional> #include <memory> class Demo:public std::enable_shared_from_this<Demo> { public: int foo(){return 0;}; int foo(int a){return 0;}; std::future<int> AsyncFoo1() { return std::async(std::launch::async, &Demo::foo, shared_from_this(), 2); // 该写法编译失败 // 已知可行修复写法: //return std::async(std::launch::async, static_cast<int(Demo::*)(int)>(&Demo::foo), shared_from_this(), 2); } std::future<int> AsyncFoo2() { return std::async(std::launch::async, [ptr=shared_from_this()](){return ptr->foo(2);}); } }; int main() { }
编译器报错信息
<source>: In member function 'std::future<int> Demo::AsyncFoo1()': <source>:13:26: error: no matching function for call to 'async(std::launch, <unresolved overloaded function type>, std::shared_ptr<Demo>, int)' 13 | return std::async(std::launch::async, &Demo::foo, shared_from_this(), 2); | ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ In file included from <source>:1: /opt/compiler-explorer/gcc-12.1.0/include/c++/12.1.0/future:1769:5: note: candidate: 'template<class _Fn, class ... _Args> std::future<typename std::__invoke_result<typename std::decay<_Tp>::type, typename std::decay<_Args>::type ...>::type> std::async(launch, _Fn&&, _Args&& ...)' 1769 | async(launch __policy, _Fn&& __fn, _Args&&... __args) | ^~~~~ /opt/compiler-explorer/gcc-12.1.0/include/c++/12.1.0/future:1769:5: note: template argument deduction/substitution failed: <source>:13:26: note: couldn't deduce template parameter '_Fn' 13 | return std::async(std::launch::async, &Demo::foo, shared_from_this(), 2); | ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /opt/compiler-explorer/gcc-12.1.0/include/c++/12.1.0/future:1803:5: note: candidate: 'template<class _Fn, class ... _Args> std::future<typename std::__invoke_result<typename std::decay<_Tp>::type, typename std::decay<_Args>::type ...>::type> std::async(_Fn&&, _Args&& ...)' 1803 | async(_Fn&& __fn, _Args&&... __args) | ^~~~~ /opt/compiler-explorer/gcc-12.1.0/include/c++/12.1.0/future:1803:5: note: template argument deduction/substitution failed: /opt/compiler-explorer/gcc-12.1.0/include/c++/12.1.0/future: In substitution of 'template<class _Fn, class ... _Args> std::future<typename std::__invoke_result<typename std::decay<_Tp>::type, typename std::decay<_Args>::type ...>::type> std::async(_Fn&&, _Args&& ...) [with _Fn = std::launch; _Args = {}]': <source>:13:26: required from here /opt/compiler-explorer/gcc-12.1.0/include/c++/12.1.0/future:1803:5: error: no type named 'type' in 'struct std::__invoke_result<std::launch>'
原因解答
- 直接传入
&Demo::foo编译失败的核心是重载歧义引发模板参数推导失败:Demo中定义了两个签名不同的同名foo成员函数,单独取&Demo::foo时,编译器看到的是一组重载函数集合,没有唯一确定的类型。而std::async是函数模板,推导可调用对象对应的模板参数_Fn时,只会检查传入实参本身的类型,不会结合后续传入的shared_from_this()、整数2等参数反推需要的成员函数签名,因此无法确定_Fn的具体类型,推导直接失败。 - 传入lambda可以正常编译,是因为lambda不存在类型歧义,且重载解析的时机完全不同:
lambda本质是编译器生成的匿名类实例,从定义完成就有唯一确定的类型,不存在重载歧义,std::async可以顺利完成模板参数推导。而ptr->foo(2)的调用写在lambda函数体内部,重载解析发生在lambda自身的编译阶段,此时编译器可以明确看到调用时传入了int类型的参数2,自然能精准匹配到int foo(int a)这个重载版本,不会把重载歧义带到std::async的模板推导环节。
内容的提问来源于stack exchange,提问作者John
相关产品推荐
相关产品推荐

