web-dev-qa-db-ja.com

c ++:エラー:「class std :: result_of <void(*(std :: unordered_map

以下は、2つのスレッドを使用してハッシュテーブルを挿入してテストする簡単なプログラムです。テストでは、ロックは使用されません。

#include <iostream>
#include <unordered_map>
#include <thread>

using namespace std;

void thread_add(unordered_map<int, int>& ht, int from, int to)
{
    for(int i = from; i <= to; ++i)
        ht.insert(unordered_map<int, int>::value_type(i, 0));
}

void test()
{
    unordered_map<int, int> ht;
    thread t[2];

    t[0] = thread(thread_add, ht, 0, 9);
    t[1] = thread(thread_add, ht, 10, 19);

    t[0].join();
    t[1].join();

    std::cout << "size: " << ht.size() << std::endl;
}

int main()
{
    test();
    return 0;
}

ただし、コンパイル時にエラーが発生します。

$ g++ -std=c++11 -pthread test.cpp
...
/usr/include/c++/4.8.2/functional:1697:61: error: no type named ‘type’ in ‘class std::result_of<void (*(std::unordered_map<int, int>, int, int))(std::unordered_map<int, int>&, int, int)>’
       typedef typename result_of<_Callable(_Args...)>::type result_type;
...

しばらくかかりましたが、まだ修正できません。ありがとう。

28
user2847598

MSVC2013でコードを正常にコンパイルできました。ただし、thread()は、引数のコピーを新しいスレッドに渡します。つまり、コンパイラでコードをコンパイルする場合、各スレッドはhtの独自のコピーで実行されるため、最後にmainhtは空になります。

GCCはこの奇妙なメッセージでコンパイルしません。スレッドで参照ラッパーを使用することで、それを取り除くことができます:

t[0] = thread(thread_add, std::ref(ht), 0, 9);
t[1] = thread(thread_add, std::ref(ht), 10, 19);

これは正常にコンパイルされます。そして、スレッドが使用する各参照は同じオブジェクトを参照します。

ただし、実行時エラーまたは予期しない結果が発生する可能性が高くなります。これは、2つのスレッドがhtに挿入しようとしているためです。しかし、unordered_mapはスレッドセーフではないため、これらのレーシング条件htが不安定な状態(つまり、UB、つまり潜在的なセグメンテーション違反)になる可能性があります。

適切に実行するには、同時アクセスを保護する必要があります。

#include <mutex>
...
mutex mtx;   // to protect against concurent access

void thread_add(unordered_map<int, int>& ht, int from, int to)
{
    for (int i = from; i <= to; ++i) {
        std::lock_guard<std::mutex> lck(mtx);  // protect statements until end of block agains concurent access
        ht.insert(unordered_map<int, int>::value_type(i, 0));
    }
}
35
Christophe

このエラーは確かに非常にわかりにくいものですが、問題はthread_addが参照によって最初のパラメーターを取得しますが、値で渡すことです。これにより、ファンクタータイプが誤って推測されます。 std::bindstd::threadのメイン関数などのファンクターへの参照によって実際に何かを渡したい場合は、参照ラッパー(std::ref)を使用する必要があります。

void test()
{
    // ...

    t[0] = thread(thread_add, std::ref(ht), 0, 9);
    t[1] = thread(thread_add, std::ref(ht), 10, 19);

    // ...
}

[ライブの例]

22
Angew