【问题标题】:Passing templated reference-parameters to a thread将模板化的参考参数传递给线程
【发布时间】:2022-01-12 21:11:45
【问题描述】:

我怎样才能使它工作?

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

我什至不明白这里有什么问题。

这是 Windows 上的 clang 13 所说的:

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.

【问题讨论】:

  • 您可以在 lambda 中使用 auto 并避免使用模板。 [](const auto &amp;t){ cout &lt;&lt; t &lt;&lt; endl; };
  • 另外,我不鼓励您在不确保线程生命周期的情况下将某些内容作为引用传递给线程
  • 让我们从您忘记包含cout 的标题开始。然后,你认为jthread 会来自哪里
  • @BonitaMontero this 为我编译了一些小的修改。
  • @BonitaMontero 我不确定你的意思。您的问题似乎是“我怎样才能使它工作?”

标签: c++ variadic-templates c++20


【解决方案1】:

只要我们坚持将thr lambda 传递给jthread 构造函数,这里的问题就有点无法解决。这个sn-p

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

将会失败,因为您不能将非常量引用绑定到 jthread 构造函数中的临时结果(注意:const PrintType&amp; hp 可以工作,但我认为这不是 OP 想要的)。

std::ref 可以使用非模板化的 lambda:

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

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

但它不适用于模板化 lambda(因为auto thr = [](auto&amp; p) 只会尝试将非 const 左值引用绑定到 reference_wrapper 类型的临时对象)。

我能想到的唯一解决方案是将带有闭包的 lambda 直接传递给线程构造函数:

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

【讨论】:

  • 有一个解决方案(在我的答案中查看第二个 sn-p)。它假设只有一个包装的引用作为参数传递(几乎总是这样)
  • @SergeyKolesnik 不,请参阅我的评论 - 您的解决方案将非常量引用转换为 const 1,但没有任何迹象表明这是允许的翻译。
  • @SergeyA:不,它没有尝试打印 reference_wrapper,因为错误不符合此要求。
  • @BonitaMontero 您的错误与打印无关,而是与绑定参考有关,正如我在回答中所解释的那样。如果将非 const 左值引用替换为 const 引用,则会看到打印错误。
  • 我已经用一个工作示例更新了我的答案,该示例用于显式模板实例化,该示例工作带有包装的引用
【解决方案2】:

TLDR:

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

您遇到的问题是由于PrintTypestd::reference_wrapper 替换造成的,这不是operator&lt;&lt; 的有效参数。您希望编译器在接收它作为参数时执行从 std::reference_wrapperstd::string&amp; 的隐式转换,但编译器不希望在那里出现 std::string&amp;。它期望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> > >}]':

如您所见,您最终不会以 std::string&amp; 作为参数,而是使用包装器:std::reference_wrapper&lt;std::__cxx11::basic_string&lt;char, std::char_traits&lt;char&gt;, std::allocator&lt;char&gt; &gt; &gt;

这段代码编译:

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

因此,合乎逻辑的解决方案是强制编译器以正确的类型实例化模板:

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

【讨论】:

  • 您的两个解决方案都使用了 const 参考,这可能不是 OP 想要的。看我的回答,用非常量引用是无法解决的。
  • @SergeyA 我假设作者要么给出了一个无效的例子(没有显示需要引用),要么不知道应该有一个 const 引用
  • 虽然您可能是正确的,但没有迹象表明这种假设是有根据的。此外,您的“解决方案”将thr 与具有get 方法的类联系在一起,对类型没有任何进一步的限制,并且可能在现实生活中无法使用。
  • @SergeyA 我更新了关于我的假设的答案。
  • @SergeyA 在现实生活中,您会在哪个线程中为函数调用传递未包装的引用?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-02-18
  • 1970-01-01
  • 2015-09-22
  • 2015-03-08
  • 2014-01-05
相关资源
最近更新 更多