使用 std::mutex 作为参数线程化成员函数

Threading a member function with a std::mutex as an argument

所以我有一个对象,它有一个我想线程化的成员函数。由于此函数将操纵对象外部的某些资源,因此我想通过引用将互斥锁作为参数传递给此函数:

#include <iostream>
#include <mutex>
#include <thread>

class foo
{
    public:
        void bar( std::mutex &m )
        {
            std::lock_guard<std::mutex> lock( m );
            std::cout << "Threading this function works!" << std::endl;
        }
};


int main()
{
    foo a;
    std::mutex m;
    std::thread bar_thread( &foo::bar, std::ref( a ), std::ref( m ) );
    bar_thread.join();
    return 0;
}

这在 Visual Studio 2013/VC++ 中编译和运行良好。但是,当我尝试在 g++ 中编译它时,它失败了。错误消息也非常含糊,这使得很难理解编译器在抱怨什么:

/usr/include/c++/4.8/functional: In instantiation of ‘struct std::_Bind_simple<std::_Mem_fn<void (foo::*)(std::mutex&)>(std::reference_wrapper<foo>, std::reference_wrapper<std::mutex>)>’:
/usr/include/c++/4.8/thread:137:47:   required from ‘std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = void (foo::*)(std::mutex&); _Args = {std::reference_wrapper<foo>, std::reference_wrapper<std::mutex>}]’
thread_test.cpp:63:69:   required from here
/usr/include/c++/4.8/functional:1697:61: error: no type named ‘type’ in ‘class std::result_of<std::_Mem_fn<void (foo::*)(std::mutex&)>(std::reference_wrapper<foo>, std::reference_wrapper<std::mutex>)>’
      typedef typename result_of<_Callable(_Args...)>::type result_type;
                                                            ^
/usr/include/c++/4.8/functional:1727:9: error: no type named ‘type’ in ‘class std::result_of<std::_Mem_fn<void (foo::*)(std::mutex&)>(std::reference_wrapper<foo>, std::reference_wrapper<std::mutex>)>’
        _M_invoke(_Index_tuple<_Indices...>)
        ^

我怀疑它与 std::mutex 的不可复制性有关,也许 g++ 中的 std::ref 实现与 vc++ 中的不同?不过这只是一个随机猜测。

是否有人精通这两种不同 C++ 编译器的微妙之处,知道是什么导致了这个问题,以及如何解决它?

当传递指向对象的指针而不是引用(包装器)时,这会在 g++ 中编译:

std::thread bar_thread( &foo::bar, &a, std::ref( m ) );

显然,正如 Richard Hodges 所回答的那样,在 C++17 之前,不支持引用包装器作为被调用方。

传递一个 reference_wrapper 作为 INVOKE 的 'this' 参数,其中 f 是一个成员函数指针在 c++17 的标准中。

到那时,它还不是严格有效的。

选择:

#include <mutex>

#include <thread>
#include <iostream>

class foo
{
    public:
        void bar( std::mutex &m )
        {
            std::lock_guard<std::mutex> lock( m );
            std::cout << "Threading this function works!" << std::endl;
        }
};


int main()
{
    foo a;
    std::mutex m;
    std::thread bar_thread( &foo::bar, std::addressof( a ), std::ref( m ) );
    bar_thread.join();
    return 0;
}

推理:

std::thread::thread<>(f, args...)是按照INVOKE(f, args...)

实现的

一些参考 material 这里:

http://en.cppreference.com/w/cpp/concept/Callable