【问题标题】:c++: error: no type named ‘type’ in ‘class std::result_of<void (*(std::unordered_mapc ++:错误:'class std :: result_of <void(*(std :: unordered_map)中没有名为'type'的类型
【发布时间】:2015-03-09 20:06:16
【问题描述】:

以下只是一个简单的程序,测试使用两个线程插入一个哈希表。测试时不使用锁。

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

花了一段时间,但仍然无法纠正。谢谢。

【问题讨论】:

标签: c++ multithreading c++11


【解决方案1】:

我可以使用 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);

这将成功编译。并且线程使用的每个引用都将引用同一个对象。

但是,您很有可能会遇到一些运行时错误或意外结果。这是因为两个线程同时尝试插入ht。但是unordered_map 不是线程安全的,因此这些竞速条件 可能会导致ht 达到不稳定状态(即UB,即潜在的segfault)。

为了让它正常运行,你必须保护你的并发访问:

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

【讨论】:

    【解决方案2】:

    这个错误确实很神秘,但问题是thread_add 通过引用获取它的第一个参数,但你通过值传递它。这会导致仿函数类型被错误地推导出来。如果你想通过引用像std::bind 这样的函子或std::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);
    
        // ...
    }
    

    [Live example]

    【讨论】:

    • 顺便说一句,clang 并没有好多少(尝试使用已删除的函数),但至少它会将您带到解压参数元组的位置,这是我猜的。
    • 不相关,但通过引用传递将在 ht 上创建数据竞争。
    • @Snps 我将 OP 的“为了测试不使用锁”的声明理解为他们打算在“上线”时提供锁。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-20
    • 1970-01-01
    • 2017-02-13
    • 2021-03-25
    • 2023-03-20
    • 1970-01-01
    相关资源
    最近更新 更多