Почему `std :: async` не может выбрать правильную перегрузку? - PullRequest
2 голосов
/ 03 февраля 2020

Пожалуйста, рассмотрите следующий пример:

#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 объект.

Я смотрел на этот вопрос здесь , но он не объясняет почему. Кроме того, я не получаю ошибку неоднозначности.

Я получаю ошибку:

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

Ответы [ 3 ]

4 голосов
/ 03 февраля 2020

Разрешение перегрузки происходит на основе типов аргументов, указанных на сайте вызова. Когда вы вызываете std::async, вы не вызываете calc_something, а передаете указатель на функцию на std::async. Нет calc_something аргументов вызова и нет способа решить, какой из двух адресов перегрузки передать на std::async.

Компилятор не может использовать последующие аргументы std::async для разрешения перегрузки. С точки зрения компилятора все std::async аргументы не связаны, и ничто не подразумевает, что они будут использоваться для вызова calc_something. Фактически, вы можете вызвать std::async с аргументами типов, отличных от тех, которые принимает calc_something, и это сработает, потому что они будут преобразованы при вызове calc_something.

Чтобы устранить эту неоднозначность, Вы должны явно привести указатель на calc_something к точному типу указателя на функцию, соответствующую одной из перегрузок.

std::async((std::size_t (*)(std::size_t))calc_something, 5);
2 голосов
/ 03 февраля 2020

std::async() является шаблоном функции , и вы передаете calc_something в качестве аргумента. Однако есть две функции, которые называются calc_something из-за перегрузки. Перегрузка calc_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);
1 голос
/ 03 февраля 2020

Наборы перегрузки не могут быть отправлены в качестве параметра как есть, они должны быть сначала преобразованы в указатель функции или должны быть переданы в объект.

Наборы перегрузки должны быть преобразованы, поскольку только имя функции обозначает полный набор функций, но вы должны отправить только одну из них. То, как компилятор выберет правильный, зависит от аргументов, которые вы ему отправили. Вызов его предоставляет тип параметров, но преобразование в указатель функции также предоставляет компилятору достаточно информации о том, какую перегрузку необходимо отправить.

Преобразование в указатель функции обычно является простым способом:

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

Подъем выполняется с помощью лямбды:

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

Подъем здесь возможен, так как вы вызываете набор перегрузки, поэтому есть параметр, который может выбрать компилятор.

Это просто не может отложить это решение для наборов перегрузки, но для лямбда-функции и функции шаблона это может.

...