为什么 std::async 不能选择正确的重载?

Why can't `std::async` pick the correct overload?

请考虑以下示例:

#include <iostream>
#include <future>

std::size_t calc_something(std::size_t lim_)
{   
    std::size_t result = lim_ * 10;
    return result;
}

void calc_something(std::size_t lim_, std::promise<std::size_t> &promise_)
{   
    std::size_t result = lim_ * 10;
    promise_.set_value(result);
}

void async_calc()
{
    std::future<std::size_t> async_calc = std::async(calc_something, 5);
    std::cout<< "async_calc = " << async_calc.get() <<std::endl;
}

我对多线程还是个新手,但为什么 - 到底 - 不能 std::async 选择正确的重载?第二个重载使用对 std::promise 对象的引用。

我看过这个问题 here 但它没有解释原因。另外,我没有收到歧义错误。

我得到的错误是:

error: no matching function for call to 'async' std::future<std::size_t> async_calc = std::async(calc_something, 5);

重载集不能原样作为参数发送,必须先转换为函数指针,或者必须提升为对象。

必须转换重载集,因为只有函数名称表示完整的函数集,但您必须只发送其中一个。编译器如何选择正确的取决于您发送给它的参数。调用它提供了参数的类型,但转换为函数指针也为编译器提供了关于必须发送哪个重载的足够信息。

转换为函数指针通常是最简单的方法:

auto function = static_cast<std::size_t(*)(std::size_t)>(calc_something);
std::future<std::size_t> async_calc = std::async(function, 5);

提升是使用 lambda 完成的:

std::future<std::size_t> async_calc = std::async([](auto lim) { return calc_something(lim); }, 5);

这里可以提升,因为你调用了重载集,所以编译器可以选择一个参数。

它不能延迟重载集的决定,但它可以延迟 lambda 和模板函数。

std::async() 是一个 函数模板 并且您将 calc_something 作为参数传递给它。但是,由于重载,有两个函数称为 calc_somethingcalc_something() 重载必须在 std::async() 参数的模板推导发生之前选择。

std::future<std::size_t> async_calc = std::async(calc_something, 5);

在上面的代码中,哪个重载将传递给 std::async()

std::size_t calc_something(std::size_t);
void calc_something(std::size_t, std::promise<std::size_t> &);

您必须指定:

std::async(static_cast<std::size_t(*)(std::size_t)>(calc_something), 5);

重载解析基于在调用站点指定的参数类型。当您调用 std::async 时,您并不是在调用 calc_something,而是将指向函数的指针传递给 std::async。没有 calc_something 调用参数,也无法解析将两个重载地址中的哪一个传递给 std::async

编译器无法使用 std::async 的后续参数来解析重载。从编译器的角度来看,所有 std::async 参数都是不相关的,没有任何暗示它们将用于调用 calc_something。事实上,您可以使用不同于 calc_something 接受的参数类型调用 std::async,并且它会起作用,因为它们会在调用 calc_something 时被转换。

为了解决这种歧义,您必须将指向 calc_something 的指针显式转换为函数指针的确切类型,匹配其中一个重载。

std::async((std::size_t (*)(std::size_t))calc_something, 5);