【问题标题】:Template with packaged_task not compile带有 packaged_task 的模板无法编译
【发布时间】:2023-04-04 21:46:01
【问题描述】:

我正在使用 Antony Williams - C++ Concurrency in Action 一书中的示例 4.14,其中使用 std::packaged_task 和 std::thread 模拟 std::async。

为什么当我取消注释行时这段代码无法编译以及如何重写模板以使其工作?

#include <iostream>
#include <future>
#include <thread>
#include <string>

void func_string(const std::string &x) {}

void func_int(int x) {}

template <typename F, typename A>
std::future<typename std::result_of<F(A&&)>::type> spawn_task(F &&f, A &&a) {
  typedef typename std::result_of<F(A&&)>::type result_type;
  std::packaged_task<result_type(A&&)> task(std::move(f));
  std::future<result_type> res(task.get_future());
  std::thread t(std::move(task), std::move(a));
  t.detach();
  return res;
}


int main () {
  std::string str = "abc";

  // auto res1 = spawn_task(func_string, str);
  // res1.get();
  auto res2 = spawn_task(func_int, 10);
  res2.get();

  return 0;
}

编译错误:

nnovzver@archer /tmp $ clang++ -std=c++11 -lpthread temp.cpp && ./a.out
In file included from temp.cpp:2:
In file included from /usr/bin/../lib64/gcc/x86_64-unknown-linux-gnu/4.8.2/../../../../include/c++/4.8.2/future:38:
/usr/bin/../lib64/gcc/x86_64-unknown-linux-gnu/4.8.2/../../../../include/c++/4.8.2/functional:1697:56: error: no type
      named 'type' in 'std::result_of<std::packaged_task<void (std::basic_string<char> &)> (std::basic_string<char>)>'
      typedef typename result_of<_Callable(_Args...)>::type result_type;
              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~
/usr/bin/../lib64/gcc/x86_64-unknown-linux-gnu/4.8.2/../../../../include/c++/4.8.2/thread:135:41: note: in instantiation
      of template class 'std::_Bind_simple<std::packaged_task<void (std::basic_string<char> &)>
      (std::basic_string<char>)>' requested here
        _M_start_thread(_M_make_routine(std::__bind_simple(
                                        ^
temp.cpp:15:15: note: in instantiation of function template specialization 'std::thread::thread<std::packaged_task<void
      (std::basic_string<char> &)>, std::basic_string<char> >' requested here
  std::thread t(std::move(task), std::move(a));
              ^
temp.cpp:24:15: note: in instantiation of function template specialization 'spawn_task<void (&)(const
      std::basic_string<char> &), std::basic_string<char> &>' requested here
  auto res1 = spawn_task(func_string, str);
              ^
In file included from temp.cpp:2:
In file included from /usr/bin/../lib64/gcc/x86_64-unknown-linux-gnu/4.8.2/../../../../include/c++/4.8.2/future:38:
/usr/bin/../lib64/gcc/x86_64-unknown-linux-gnu/4.8.2/../../../../include/c++/4.8.2/functional:1726:50: error: no type
      named 'type' in 'std::result_of<std::packaged_task<void (std::basic_string<char> &)> (std::basic_string<char>)>'
        typename result_of<_Callable(_Args...)>::type
        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~
2 errors generated.

【问题讨论】:

  • 能否提供编译错误?
  • 编译可以像这样改变函数签名:void func_string(std::string &amp;&amp;x)并使用std::move(str)。我仍然不确定在一般情况下如何正确处理完美转发……
  • @galop1n 这是一个技巧,因为这很完美auto res = async(launch::async, func_string, str);,我正在尝试效仿。

标签: c++ templates c++11 type-deduction


【解决方案1】:

如果您查看 async 的 documentation,您想要模拟的功能,它会衰减所有未来的模板参数。而且它很有效,因为它像这样工作得更好,如here 所示:

template <typename T_>
using decay_t = typename std::decay<T_>::type;
template< class T >
using result_of_t = typename std::result_of<T>::type;

template <typename F, typename A>
std::future<result_of_t<decay_t<F>(decay_t<A>)>> spawn_task(F &&f, A &&a) {
    using result_t = result_of_t<decay_t<F>(decay_t<A>)>;
    std::packaged_task< result_t(decay_t<A>)> task(std::forward<F>(f));
    auto res = task.get_future();
    std::thread t(std::move(task), std::forward<A>(a));
    t.detach();
    return res;
}

【讨论】:

  • 我实际上在一行中进行了更改,它起作用了std::packaged_task&lt;result_type(decay_t&lt;A&gt;)&gt; task(std::move(f));。没有std::forward,本书的例子也毫无用处。
  • @nnovzver std::move 不好,因为如果你传入一个非右值元素,你会从中得到move。这是非常粗鲁的。
【解决方案2】:

首先std::packaged_task 包装了任何Callable 目标函数。而Callable的要求之一是它有一个合适的返回值。 void 不是合适的返回值。我将您的函数更改为返回 int 用于测试目的:

int func_string(const std::string &x) 
{
    return 1;
}

int func_int(int x) 
{
    return 2;
}

其次,在我看来,您的实际函数签名与您在 std::result_of&lt;&gt; 模板中指定的内容不匹配。具体来说,您的函数签名是:

int func_string(const std::string &x);
int func_int(int x);

但你正在通过:

std::result_of<F(A&&)>::type result_type

将其更改为:

std::result_of<F(A)>::type result_type

一切都会编译。

这是完整的源代码(减去标题):

int func_string(const std::string &x) 
{
    return 1;
}

int func_int(int x) 
{
    return 2;
}

template <typename F, typename A>
std::future<typename std::result_of<F(A)>::type> spawn_task(F &&f, A &&a) 
{
    typedef typename std::result_of<F(A)>::type result_type;
    std::packaged_task<result_type(A)> task(std::move(f));
    std::future<result_type> res(task.get_future());
    std::thread t(std::move(task), std::move(a));
    t.detach();
    return res;
}

int main()
{
    std::string str = "abc";

     auto res1 = spawn_task(func_string, str);
     res1.get();
    auto res2 = spawn_task(func_int, 10);
    res2.get();
}

【讨论】:

  • @galop1n 有趣 - 这可以在 VS2013 上正确编译和运行。
  • 永远不要相信 VS 编译器...
  • 为什么void 不是一个合适的返回类型?其次,A&amp;&amp; 是传递给spawn_task 的参数类型(不必是右值引用——该技术称为完美转发或通用引用)。在给定的情况下,A&amp;&amp;std::string const&amp; &amp;&amp;,它解析为std::string const&amp;。问题出在其他地方,当 OP 在通用参考上执行 std::move...
猜你喜欢
  • 1970-01-01
  • 2012-06-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-29
  • 1970-01-01
  • 1970-01-01
  • 2023-03-03
相关资源
最近更新 更多