为什么"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); //Why this line does not compile, whereas `AsyncFoo2()` which uses lambda works well ? I know the blow one is right.
//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); //Why this line does not compile, whereas `AsyncFoo2()` which uses lambda works well ? I know the blow one is right.
|                ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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); //Why this line does not compile, whereas `AsyncFoo2()` which uses lambda works well ? I know the blow one is right.
|                ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/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>'

这是语言的限制。实际上,问题是不能取重载集的地址,只能取函数的地址,它与std::async无关。因此,如果foo方法过载,则&Demo::foo无效。实际上,如果foo是一个函数模板,并且您没有显式地提供模板参数,也会发生同样的问题。

这也意味着不能在语言中制作一个完美的包装来推迟调用,因为这正是std::async理想情况下想要做的

Lambdas完全避开了这个问题,你永远不会在lambda中获取地址,你只需要用正常语法调用函数,该函数具有由语言烘焙的重载解决方案。这是因为lambdas是AFAIK,它是唯一允许在表达式中动态创建类型和函数(调用运算符)的构造。

此外,Lambda可以四处传递,因为它们只是对象。

Lambdas甚至更强大,因为您可以将调用模板化,并且仍然可以完全出于同样的原因传递lambda——您传递对象,永远不会获取模板化operator()的地址。

到目前为止,还没有一个无宏的解决方案,它必须来自语言。有一些建议,我知道P1170R0,不幸的是,到目前为止还没有被接受。我发现的最后一条消息来自github

CONSENSUS:LEWGI想要一个可变的std::overload_set库API(如P1772所述)。P1170和P1772的作者应该合作实现这一目标。

该提案还包含一个临时解决方案,用于根据函数名称构建新的lambda:

#define FWD(x) static_cast<decltype(x)&&>(x)
#define RETURNS(expr) noexcept(noexcept(expr)) -> decltype(expr) { return expr; }
#define OVERLOADS_OF(name) [&](auto&& ...args) RETURNS(name(FWD(args)...))

但我不确定它是否能很容易地适应成员函数,尤其是如果您通过共享ptr调用它们。

相关内容

  • 没有找到相关文章

最新更新