2

So I have an object with a member function I want to thread. Since this function will be manipulating some resource outside of the object, I want to pass a mutex by reference as an argument to this function:

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

This compiles and runs fine in Visual Studio 2013/VC++. However when I try and compile this in g++ it fails. The error message is also very cryptic which makes it very difficult to understand what the compiler is complaining about:

/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...>)
        ^

I have a suspicion it has something to do with the non-copyability of std::mutex and perhaps the std::ref implementation in g++ is different to the one in vc++? This is just a random guess though.

Does anyone who is well versed in the subtleties of the two different C++ compilers know what is causing this problem, and how it can be resolved?

Fat-chunk
  • 361
  • 5
  • 14

2 Answers2

0

This compiles in g++ when a pointer to the object is passed instead of a reference (wrapper):

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

Apparently, as answered by Richard Hodges, reference wrappers are not supported as callee until C++17.

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • it's not a bug unless it does not work with -std=c++17 – Richard Hodges Apr 07 '16 at 09:56
  • @RichardHodges I'm not familiar with that part of the standard, but I take your and cppreference's word for it. – eerorika Apr 07 '16 at 09:57
  • 1
    i think it's more an accident of history than anything. It's just that a reference wrapper being arg2 of the imaginary INVOKE function was not mentioned in any previous standard (presumably because no-one thought they'd need it). std::invoke() exists as a function as of c++17. It's had a facelift as a result. – Richard Hodges Apr 07 '16 at 10:09
0

passing a reference_wrapper as the 'this' argument of INVOKE where f is a member function pointer is in the standard as of c++17.

Until then, it's not strictly valid.

alternative:

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

Reasoning:

std::thread::thread<>(f, args...) is implemented in terms of INVOKE (f, args...)

some reference material here:

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

Richard Hodges
  • 68,278
  • 7
  • 90
  • 142