Как мне создать packaged_task с параметрами?
Следуя этому превосходному учебнику по будущим, обещаниям и пакетным задачам, я дошел до того, что хотел подготовить свое собственное задание.
#include <iostream>
#include <future>
using namespace std;
int ackermann(int m, int n) { // might take a while
if(m==0) return n+1;
if(n==0) return ackermann(m-1,1);
return ackermann(m-1, ackermann(m, n-1));
}
int main () {
packaged_task<int(int,int)> task1 { &ackermann, 3, 11 }; // <- error
auto f1 = task1.get_future();
thread th1 { move(task1) }; // call
cout << " ack(3,11):" << f1.get() << endl;
th1.join();
}
Насколько я могу расшифровать сообщение об ошибке gcc-4.7.0, оно ожидает аргументы по-разному? Но как? Я пытаюсь сократить сообщение об ошибке:
error: no matching function for call to
'std::packaged_task<int(int, int)>::packaged_task(<brace-enclosed initializer list>)'
note: candidates are:
std::packaged_task<_Res(_ArgTypes ...)>::---<_Res(_ArgTypes ...)>&&) ---
note: candidate expects 1 argument, 3 provided
...
note: cannot convert 'ackermann'
(type 'int (*)(int, int)') to type 'std::allocator_arg_t'
Является ли мой вариант, как я предоставляю параметры для ackermann
неправильно? Или это неверный параметр шаблона? Я не даю параметры 3,11
на создание темы, верно?
Обновите другие неудачные варианты:
packaged_task<int()> task1 ( []{return ackermann(3,11);} );
thread th1 { move(task1) };
packaged_task<int()> task1 ( bind(&ackermann,3,11) );
thread th1 { move(task1) };
packaged_task<int(int,int)> task1 ( &ackermann );
thread th1 { move(task1), 3,11 };
хм... это я или бета-gcc?
2 ответа
Во-первых, если вы объявите std::packaged_task
чтобы принять аргументы, то вы должны передать их operator()
не конструктор. Таким образом, в одном потоке вы можете сделать:
std::packaged_task<int(int,int)> task(&ackermann);
auto f=task.get_future();
task(3,11);
std::cout<<f.get()<<std::endl;
Чтобы сделать то же самое с потоком, вы должны переместить задачу в поток и также передать аргументы:
std::packaged_task<int(int,int)> task(&ackermann);
auto f=task.get_future();
std::thread t(std::move(task),3,11);
t.join();
std::cout<<f.get()<<std::endl;
В качестве альтернативы, вы можете связать аргументы непосредственно перед созданием задачи, и в этом случае сама задача теперь имеет подпись, которая не принимает аргументов:
std::packaged_task<int()> task(std::bind(&ackermann,3,11));
auto f=task.get_future();
task();
std::cout<<f.get()<<std::endl;
Опять же, вы можете сделать это и передать его в поток:
std::packaged_task<int()> task(std::bind(&ackermann,3,11));
auto f=task.get_future();
std::thread t(std::move(task));
t.join();
std::cout<<f.get()<<std::endl;
Все эти примеры должны работать (и работают как с g++ 4.6, так и с MSVC2010 и моей реализацией библиотеки потоков : just::thread). Если нет, то есть ошибка в компиляторе или библиотеке, которую вы используете. Например, библиотека, поставляемая с g++ 4.6, не может обрабатывать передаваемые объекты только для перемещения, такие как std::packaged_task
в std::thread
(и, следовательно, не обрабатывает 2-й и 4-й примеры), так как он использует std::bind
в качестве детали реализации, и что реализация std::bind
неправильно требует, чтобы аргументы были копируемыми.
Поскольку вы запускаете поток без аргументов, вы ожидаете, что задача будет запущена без аргументов, как если бы task1()
были использованы. Следовательно, подпись, которую вы хотите поддержать, не является int(int, int)
но int()
, В свою очередь это означает, что вы должны передать функтор, совместимый с этой подписью, в конструктор std::packaged_task<int()>
, Пытаться:
packaged_task<int()> task1 { std::bind(&ackermann, 3, 11) };
Другая возможность:
packaged_task<int(int,int)> task1 { &ackermann };
auto f1 = task1.get_future();
thread th1 { move(task1), 3, 11 };
потому что конструктор std::thread
могу принять аргументы. Здесь функтор, который вы передаете ему, будет использоваться как task1(3, 11)
были использованы.