c++visual-studiovisual-c++visual-studio-2015visual-c++-2015

Visual C++ 14 - cannot build a simple thread program which builds on Visual C++ 12


I have this MCVE

#include <iostream>
#include <thread>

class Foo
{
    void bar_i() { std::cout << "hello" << std::endl; }

public:
    void bar()
    {
        // I don't know why I used std::ref(*this) instead of this, I wrote this some time ago
        std::thread t(&Foo::bar_i, std::ref(*this));
        t.join();
    }
};

int main()
{
    Foo f;
    f.bar();
}

which builds on VS 2013 Update 3 and Coliru, but does not build on VS 2015 with this error list:

1>c:\program files (x86)\microsoft visual studio 14.0\vc\include\thr\xthread(238): error C2893: Failed to specialize function template 'unknown-type std::invoke(_Callable &&,_Types &&...)'
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\thr\xthread(238): note: With the following template arguments:
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\thr\xthread(238): note: '_Callable=void (__thiscall Foo::* )(void)'
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\thr\xthread(238): note: '_Types={std::reference_wrapper<Foo>}'
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\thr\xthread(247): note: see reference to function template instantiation 'void std::_LaunchPad<_Target>::_Execute<0,1>(std::tuple<void (__thiscall Foo::* )(void),std::reference_wrapper<Foo>> &,std::integer_sequence<_Ty,0,1>)' being compiled
1>          with
1>          [
1>              _Target=std::unique_ptr<std::tuple<void (__thiscall Foo::* )(void),std::reference_wrapper<Foo>>,std::default_delete<std::tuple<void (__thiscall Foo::* )(void),std::reference_wrapper<Foo>>>>,
1>              _Ty=size_t
1>          ]
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\thr\xthread(247): note: see reference to function template instantiation 'void std::_LaunchPad<_Target>::_Execute<0,1>(std::tuple<void (__thiscall Foo::* )(void),std::reference_wrapper<Foo>> &,std::integer_sequence<_Ty,0,1>)' being compiled
1>          with
1>          [
1>              _Target=std::unique_ptr<std::tuple<void (__thiscall Foo::* )(void),std::reference_wrapper<Foo>>,std::default_delete<std::tuple<void (__thiscall Foo::* )(void),std::reference_wrapper<Foo>>>>,
1>              _Ty=size_t
1>          ]
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\thr\xthread(242): note: while compiling class template member function 'void std::_LaunchPad<_Target>::_Run(std::_LaunchPad<_Target> *) noexcept'
1>          with
1>          [
1>              _Target=std::unique_ptr<std::tuple<void (__thiscall Foo::* )(void),std::reference_wrapper<Foo>>,std::default_delete<std::tuple<void (__thiscall Foo::* )(void),std::reference_wrapper<Foo>>>>
1>          ]
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\thr\xthread(230): note: see reference to function template instantiation 'void std::_LaunchPad<_Target>::_Run(std::_LaunchPad<_Target> *) noexcept' being compiled
1>          with
1>          [
1>              _Target=std::unique_ptr<std::tuple<void (__thiscall Foo::* )(void),std::reference_wrapper<Foo>>,std::default_delete<std::tuple<void (__thiscall Foo::* )(void),std::reference_wrapper<Foo>>>>
1>          ]
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\thr\xthread(256): note: see reference to class template instantiation 'std::_LaunchPad<_Target>' being compiled
1>          with
1>          [
1>              _Target=std::unique_ptr<std::tuple<void (__thiscall Foo::* )(void),std::reference_wrapper<Foo>>,std::default_delete<std::tuple<void (__thiscall Foo::* )(void),std::reference_wrapper<Foo>>>>
1>          ]
1>  c:\program files (x86)\microsoft visual studio 14.0\vc\include\thread(52): note: see reference to function template instantiation 'void std::_Launch<std::unique_ptr<std::tuple<void (__thiscall Foo::* )(void),std::reference_wrapper<Foo>>,std::default_delete<std::tuple<void (__thiscall Foo::* )(void),std::reference_wrapper<Foo>>>>>(_Thrd_t *,_Target &&)' being compiled
1>          with
1>          [
1>              _Target=std::unique_ptr<std::tuple<void (__thiscall Foo::* )(void),std::reference_wrapper<Foo>>,std::default_delete<std::tuple<void (__thiscall Foo::* )(void),std::reference_wrapper<Foo>>>>
1>          ]
1>  d:\libraries\c++ active\tests\tests\test.cpp(13): note: see reference to function template instantiation 'std::thread::thread<void(__thiscall Foo::* )(void),std::reference_wrapper<Foo>,void>(_Fn &&,std::reference_wrapper<Foo> &&)' being compiled
1>          with
1>          [
1>              _Fn=void (__thiscall Foo::* )(void)
1>          ]

If not for the std::ref, this code would compile. What seems to be the problem?


Solution

  • As far as I can tell, the rules in 20.9.2 cause your code to be equivalent to

    auto f = &Foo::bar_i;
    decay<decltype(std::ref(*this))>::type t1 = std::ref(*this); // still reference_wrapper
    ((*t1).*f)();
    

    which is in fact an error.

    Just use std::thread t(&Foo::bar_i, this);, since the pointer-to-member-function machinery present in std::thread knows to dereference the first parameter when performing the call.

    According to the draft n4527 I'm looking at, the special handling of std::reference_wrapper used by std::bind (see 20.9.10.3) is not applied to std::thread. Neither is the special decay logic used by std::tuple, which causes reference_wrapper to become a normal reference.