在无序映射中访问 class 实例方法导致编译错误

Accessing class instance method in unordered map causes compile error

概览

我正在用 C++ 实现生产者-消费者模型。我有多个消费者,每个消费者都有自己的线程和队列。为了方便访问,我想要一个表单 的映射,这样我就可以在向它推送数据时通过查找简单地获取消费者线程。尝试访问存储实例方法时编译器抛出错误。

代码

消费者 class 启动一个带有附加 FIFO 队列的线程,并等待项目被推入(队列头在此处可用:https://github.com/cameron314/readerwriterqueue/blob/master/readerwriterqueue.h

// consumer.h
#include <functional>
#include <thread>
#include "readerwriterqueue.h"

// TYPE HAS TO BE A POINTER
template<typename T>
class Consumer
{
public:
    Consumer(int id, std::function<void(int, T&)> func) :
        m_id(id), m_func(func) {}

    ~Consumer(){
        m_running = false;
        this->pushBack(nullptr);
        m_thread.join();
    }

    // satisfy rule of three; disallow copying as we manage a thread
    Consumer(const Consumer&) = delete;
    Consumer& operator=(const Consumer&) = delete;

    void pushBack(const T& t){
        m_BufferQueue.enqueue(t);
    }

private:

    void work() {
        m_running = true;

        while(m_running || m_BufferQueue.peek())
        {
            T t;
            m_BufferQueue.wait_dequeue(t);

            if (t == nullptr)
                break;

            m_func(m_id, t);
        }
    }

    int m_id;
    std::function<void(int, T&)> m_func;
    moodycamel::BlockingReaderWriterQueue<T> m_BufferQueue{64};
    std::thread m_thread{&Consumer::work, this};
    std::atomic_bool m_running;
};

我想创建此 class 的多个实例并将它们放入无序映射中,这样我就可以在必要时将它们推入队列。据我所知,emplace 帮助我避免编写移动 constructor/assignment 运算符,因为它会直接在地图中创建和放置对象。

// main_player.cpp
#include <iostream>
#include <unordered_map>
#include "consumer.h"

void func(int id, int* val)
{
    std::cout << "Thread " << id << " received value " << *val << "\n";
}

int main(int argc, char** argv) {

    std::unordered_map<int, Consumer<int*>> consumers;

    consumers.emplace(std::piecewise_construct, std::forward_as_tuple(0), std::forward_as_tuple(0, func));

    // push into queues here and let the threads do the work
    for(int i = 0; i < 5; i++)
        consumers[0].pushBack(&i)

    return 0;
}

编译器在返回队列时出现问题。从错误日志中,我可以推断他似乎确实构造了另一个实例并失败了。有人可以帮我吗?

错误日志

In file included from /usr/include/c++/7/functional:54:0,
                 from ../consumer.h:4,
                 from ../main_player.cpp:31:
/usr/include/c++/7/tuple: In instantiation of ‘std::pair<_T1, _T2>::pair(std::tuple<_Args1 ...>&, std::tuple<_Args2 ...>&, std::_Index_tuple<_Indexes1 ...>, std::_Index_tuple<_Indexes2 ...>) [with _Args1 = {int&&}; long unsigned int ..._Indexes1 = {0}; _Args2 = {}; long unsigned int ..._Indexes2 = {}; _T1 = const int; _T2 = Consumer<int*>]’:
/usr/include/c++/7/tuple:1641:63:   required from ‘std::pair<_T1, _T2>::pair(std::piecewise_construct_t, std::tuple<_Args1 ...>, std::tuple<_Args2 ...>) [with _Args1 = {int&&}; _Args2 = {}; _T1 = const int; _T2 = Consumer<int*>]’
/usr/include/c++/7/ext/new_allocator.h:136:4:   required from ‘void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = std::pair<const int, Consumer<int*> >; _Args = {const std::piecewise_construct_t&, std::tuple<int&&>, std::tuple<>}; _Tp = std::pair<const int, Consumer<int*> >]’
/usr/include/c++/7/bits/alloc_traits.h:475:4:   required from ‘static void std::allocator_traits<std::allocator<_CharT> >::construct(std::allocator_traits<std::allocator<_CharT> >::allocator_type&, _Up*, _Args&& ...) [with _Up = std::pair<const int, Consumer<int*> >; _Args = {const std::piecewise_construct_t&, std::tuple<int&&>, std::tuple<>}; _Tp = std::pair<const int, Consumer<int*> >; std::allocator_traits<std::allocator<_CharT> >::allocator_type = std::allocator<std::pair<const int, Consumer<int*> > >]’
/usr/include/c++/7/bits/hashtable_policy.h:2066:37:   required from ‘std::__detail::_Hashtable_alloc<_NodeAlloc>::__node_type* std::__detail::_Hashtable_alloc<_NodeAlloc>::_M_allocate_node(_Args&& ...) [with _Args = {const std::piecewise_construct_t&, std::tuple<int&&>, std::tuple<>}; _NodeAlloc = std::allocator<std::__detail::_Hash_node<std::pair<const int, Consumer<int*> >, false> >; std::__detail::_Hashtable_alloc<_NodeAlloc>::__node_type = std::__detail::_Hash_node<std::pair<const int, Consumer<int*> >, false>]’
/usr/include/c++/7/bits/hashtable_policy.h:750:8:   required from ‘std::__detail::_Map_base<_Key, _Pair, _Alloc, std::__detail::_Select1st, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits, true>::mapped_type& std::__detail::_Map_base<_Key, _Pair, _Alloc, std::__detail::_Select1st, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits, true>::operator[](std::__detail::_Map_base<_Key, _Pair, _Alloc, std::__detail::_Select1st, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits, true>::key_type&&) [with _Key = int; _Pair = std::pair<const int, Consumer<int*> >; _Alloc = std::allocator<std::pair<const int, Consumer<int*> > >; _Equal = std::equal_to<int>; _H1 = std::hash<int>; _H2 = std::__detail::_Mod_range_hashing; _Hash = std::__detail::_Default_ranged_hash; _RehashPolicy = std::__detail::_Prime_rehash_policy; _Traits = std::__detail::_Hashtable_traits<false, false, true>; std::__detail::_Map_base<_Key, _Pair, _Alloc, std::__detail::_Select1st, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits, true>::mapped_type = Consumer<int*>; std::__detail::_Map_base<_Key, _Pair, _Alloc, std::__detail::_Select1st, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits, true>::key_type = int]’
/usr/include/c++/7/bits/unordered_map.h:977:20:   required from ‘std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::mapped_type& std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::operator[](std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::key_type&&) [with _Key = int; _Tp = Consumer<int*>; _Hash = std::hash<int>; _Pred = std::equal_to<int>; _Alloc = std::allocator<std::pair<const int, Consumer<int*> > >; std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::mapped_type = Consumer<int*>; std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::key_type = int]’
../main_player.cpp:48:20:   required from here
/usr/include/c++/7/tuple:1652:70: error: no matching function for call to ‘Consumer<int*>::Consumer()’
         second(std::forward<_Args2>(std::get<_Indexes2>(__tuple2))...)
                                                                      ^
In file included from ../main_player.cpp:31:0:
../consumer.h:13:5: note: candidate: Consumer<T>::Consumer(int, std::function<void(int, T&)>) [with T = int*]
     Consumer(int id, std::function<void(int, T&)> func) :
     ^~~~~~~~
../consumer.h:13:5: note:   candidate expects 2 arguments, 0 provided
make: *** [main_player.o] Error 1
subdir.mk:66: recipe for target 'main_player.o' failed
"make -j20 all" terminated with exit code 2. Build might be incomplete.

18:01:03 Build Failed. 3 errors, 0 warnings. (took 739ms)

您需要在 class Consumer 中添加 默认构造函数 ,如错误所述

error: no matching function for call to ‘Consumer<int*>::Consumer()’

您可以通过两种方式进行:

方法一

// consumer.h
#include <functional>
#include <thread>
#include "readerwriterqueue.h"

// TYPE HAS TO BE A POINTER
template<typename T>
class Consumer
{
public:
    Consumer(int id, std::function<void(int, T&)> func) :
        m_id(id), m_func(func) {}

    //other member here
    
    //ADD THE DEFAULT CONSTRUCTOR 
    Consumer() = default;
    

private:

    //...

    int m_id = 0; //USE IN-CLASS INITIALIZER for built in type
    //other members here
};

方法二

// consumer.h
#include <functional>
#include <thread>
#include "readerwriterqueue.h"

// TYPE HAS TO BE A POINTER
template<typename T>
class Consumer
{
public:
    Consumer(int id, std::function<void(int, T&)> func) :
        m_id(id), m_func(func) {}

    //other member here
    
    //ADD THE DEFAULT CONSTRUCTOR 
    Consumer(): m_id(0)
    {
    }
    

private:

    //...

    int m_id;
    //other members here
};