Передача аргументов в std:: async по ссылке не выполняется - программирование
Подтвердить что ты не робот

Передача аргументов в std:: async по ссылке не выполняется

Я заметил, что невозможно передать неконстантную ссылку в качестве аргумента std::async.

#include <functional>
#include <future>

void foo(int& value) {}

int main() {
    int value = 23;
    std::async(foo, value);
}

Мой компилятор (GCC 4.8.1) дает следующую ошибку для этого примера:

error: no type named ‘type’ in ‘class std::result_of<void (*(int))(int&)>’

Но если я переношу значение, переданное в std::async в std::reference_wrapper, все в порядке. Я предполагаю, что это потому, что std::async принимает аргументы по значению, но я до сих пор не понимаю причины ошибки.

4b9b3361

Ответ 1

Это преднамеренный выбор дизайна/компромисс.

Во-первых, не обязательно выяснить, переносится ли функциональный объект в async его аргументы по ссылке или нет. (Если это не простая функция, а объект функции, у нее может быть, например, перегруженный оператор вызова функции.) Таким образом, async не может сказать: "Эй, позвольте мне просто проверить, чего хочет целевая функция, и я сделаю правильная вещь."

Итак, вопрос о дизайне: действительно ли он принимает все аргументы по ссылке (т.е. если они являются lvalues) или всегда делает копии? Создание копий - это безопасный выбор: копия не может обернуться, и копия не может отображать условия гонки (если это не очень странно). Так что выбор, который был сделан: все аргументы копируются по умолчанию.

Но тогда механизм записывается так, что он фактически не может передать аргументы не-const lvalue reference parameter. Это еще один выбор для безопасности: в противном случае функция, которую вы ожидали бы изменить исходную lvalue, вместо этого изменит копию, что приведет к ошибкам, которые очень трудно отследить.

Но что, если вы действительно, действительно хотите не константный ссылочный параметр lvalue? Что, если вы обещаете следить за обвисшими ссылками и условиями гонки? Для этого нужен std:: ref. Это явный отказ от опасной ссылочной семантики. Это ваш способ сказать: "Я знаю, что я здесь делаю".

Ответ 2

std::async (и другие функции, которые делают идеальную переадресацию), посмотрите на тип аргумента, который вы передаете, чтобы выяснить, что делать. Они не рассматривают, как этот аргумент будет в конечном итоге использоваться. Итак, чтобы передать объект по ссылке, вам нужно сообщить std::async, что вы используете ссылку. Однако просто передать ссылку не будет. Вы должны использовать std::ref(value) для передачи value по ссылке.

Ответ 3

Сама проблема лишь незначительно связана с std::async(): при определении результата операции std::async() использует std::result_of<...>::type со всеми аргументами std::decay<...>::type 'ed. Это разумно, потому что std::async() принимает произвольные типы и пересылает их для хранения в каком-либо месте. Чтобы сохранить их, необходимы значения как для объекта функции, так и для аргументов. Таким образом, std::result_of<...> используется аналогично этому:

typedef std::result_of<void (*(int))(int&)>::type result_type;

... и так как int не может быть привязан к int& (int не является значением типа lvalue для привязки к int&), это терпит неудачу. Отказ в этом случае означает, что std::result_of<...> не определяет вложенный type.

Следующий вопрос может быть следующим: что этот тип используется для создания экземпляра std::result_of<...>? Идея заключается в том, что используется синтаксис вызова функции, состоящий из ResultType(ArgumentTypes...): вместо типа результата передается тип функции и std::result_of<...> определяет тип функции, вызываемой при вызове этого типа функции с заданным списком аргументов называется. Для типов указателей функций это не очень интересно, но тип функции также может быть объектом функции, в котором необходимо учитывать перегрузку. Итак, в основном, std::result_of<...> используется следующим образом:

typedef void (*function_type)(int&);
typedef std::result_of<function_type(int)>::type result_type; // fails
typedef std::result_of<function_type(std::reference_wrapper<int>)>::type result_type; //OK