Home > Blockchain >  Passing templated reference-parameters to a thread
Passing templated reference-parameters to a thread

Time:12-08

How could I make this working ?

#include <iostream>
#include <thread>

using namespace std;

int main()
{
    auto thr = []<typename PrintType>( PrintType &p )
    {
        cout << p << endl;
    };
    string str = "hello world";
    jthread jt( thr, ref( str ) );
}

I don't even understand what's the issue here.

This is what clang 13 on Windows says:

In file included from test.cpp:2:
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.30.30705\include\thread:55:9: error: no matching function for call to 'invoke'
        _STD invoke(_STD move(_STD get<_Indices>(_Tup))...);
        ^~~~~~~~~~~
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.30.30705\include\yvals_core.h:1385:20: note: expanded from macro '_STD'
#define _STD       ::std::
                   ^
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.30.30705\include\thread:62:17: note: in instantiation of function template specialization 'std::thread::_Invoke<std::tuple<(lambda at test.cpp:8:13), std::reference_wrapper<std::basic_string<char>>>, 0ULL, 1ULL>' requested here
        return &_Invoke<_Tuple, _Indices...>;
                ^
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.30.30705\include\thread:302:19: note: in instantiation of function template specialization 'std::thread::_Start<(lambda at test.cpp:8:13) &, std::reference_wrapper<std::basic_string<char>>>' requested here
            _Impl._Start(_STD forward<_Fn>(_Fx), _STD forward<_Args>(_Ax)...);
                  ^
test.cpp:13:10: note: in instantiation of function template specialization 'std::jthread::jthread<(lambda at test.cpp:8:13) &, std::reference_wrapper<std::basic_string<char>>, 0>' requested here
        jthread jt( thr, ref( str ) );
                ^
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.30.30705\include\type_traits:1482:19: note: candidate template ignored: substitution failure [with _Callable = (lambda at test.cpp:8:13), _Ty1 = std::reference_wrapper<std::basic_string<char>>, _Types2 = <>]: no matching function for call to '_Call'
_CONSTEXPR17 auto invoke(_Callable&& _Obj, _Ty1&& _Arg1, _Types2&&... _Args2) noexcept(
                  ^
C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.30.30705\include\type_traits:1476:19: note: candidate function template not viable: requires single argument '_Obj', but 2 arguments were provided
_CONSTEXPR17 auto invoke(_Callable&& _Obj) noexcept(noexcept(static_cast<_Callable&&>(_Obj)()))
                  ^
1 error generated.

CodePudding user response:

The problem here is kinda unsolvable as long as we insist on passing thr lambda to jthread constructor. This snippet

auto thr = []<typename PrintType>( PrintType &p )
{
    cout << p << endl;
};
string str = "hello world";
jthread jt(thr, str);

is going to fail, because you can't bind non-const reference to a temporary resulting within jthread constructor (Note: const PrintType& hp would work, but I assume, it would not be what OP wants).

std::ref would work with non-templated lambda:

auto thr = [](std::string& p)
{
    std::cout << p << std::endl;
};

std::string str = "hello world";
std::thread jt( thr, std::ref(str) );

But it won't work with templated lambda (becauseauto thr = [](auto& p) will just try to bind non-const lvalue reference to temporary object of reference_wrapper type).

The only solution I can think of is to pass lambda with closures directly to thread constructor:

auto thr = []<class Type>(Type& p)
{
    std::cout << p << std::endl;
};
std::string str = "hello world";
std::thread jt([&str, &thr]() { thr(str); });

CodePudding user response:

TLDR:

    jthread jt(&decltype(thr)::operator()<std::string>, thr, ref(str));

The problem you are having is due to a substitution of PrintType by std::reference_wrapper, which is not recognised as a valid argument for operator<<. You expect the compiler to do an implicit conversion from std::reference_wrapper to std::string& when receiving it as an argument, but the compiler does not expect an std::string& there. It expects std::reference_wrapper.

/opt/compiler-explorer/gcc-11.2.0/include/c  /11.2.0/thread: In instantiation of 'static std::thread std::jthread::_S_create(std::stop_source&, _Callable&&, _Args&& ...) [with _Callable = main()::<lambda(PrintType&)>&; _Args = {std::reference_wrapper<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >}]':

As you can see, you don't end up with std::string& as an argument, but with a wrapper: std::reference_wrapper<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >.

This code compiles:

#include <thread>
#include <iostream>

using namespace std;

int main()
{
    auto thr = [](const std::string &p)
    {
        cout << p << endl;
    };
    string str = "hello world";
    jthread jt(thr, ref( str ));
}

So, the logical solution would be to force the compiler to instantiate the template with the right type:

#include <thread>
#include <iostream>
#include <type_traits>

using namespace std;

int main()
{
    auto thr = []<typename PrintType>(PrintType& p)
    {
        cout << p << endl;
    };
    string str = "hello world";

    // explicitly instantiating via a pointer to member function
    jthread jt(&decltype(thr)::operator()<std::string>, thr, ref(str));
}
  • Related