9

futurespromise、およびパッケージ化されたタスクに関するこの優れたチュートリアルに従って、自分のタスクを準備したいと思うようになりました。

#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  };

うーん...それは私ですか、それともbeta-gccですか?

4

2 に答える 2

21

まず、引数を取ることを宣言する場合は、コンストラクタではなく に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 の両方、およびスレッド ライブラリの my just::thread実装で動作します)。そうでない場合は、使用しているコンパイラまたはライブラリにバグがあります。たとえば、g++ 4.6 に同梱されているライブラリは、std::packaged_tasktoなどの移動のみのオブジェクトの受け渡しを処理できませんstd::thread(したがって、2 番目と 4 番目の例を処理できません)。コピー可能です。std::bindstd::bind

于 2011-09-25T21:58:06.513 に答える
3

引数なしでスレッドを開始しているので、使用されたかのように、タスクが引数なしで開始されることを期待します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)に使用されます。

于 2011-09-25T20:53:48.963 に答える