c++ - std::async 与共享指针模板化成员函数

标签 c++ multithreading templates asynchronous

我正致力于在一个项目中实现多线程,但是当涉及到 std::async 的一些更复杂的使用时,我遇到了困难。我想在模板化对象上调用成员函数并将参数作为参数传递,但是当模板包含共享指针时我无法让它工作。

在成员函数上使用 std::async 非常简单,甚至是模板化的成员函数。对于这种特定用法,我已经看到了很多关于堆栈溢出的答案。我什至自己运行了一些测试用例:

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

class Bar
{
  public:
    Bar () {}
    double data;
};

template <typename T>
class Foo
{
  public:
    Foo () {}
    T set_data (T d) { data = d; return data; }
  private:
    T data;
};

#include "./foo.h"
#include <memory>

int main (int argc, char **argv)
{
  /**
   * Works fine
   */
  Foo<int> foo1;
  auto fut1 = std::async(std::launch::async, &Foo<int>::set_data, &foo1, 42);

  fut1.wait();
  std::cout << fut1.get() << std::endl;

  return 0;
}

此示例在 gcc 7.4.0 中编译得很好,并按预期返回 42。

当我在模板中使用 shared_ptr 时,我的问题就来了。使用上面相同的 Foo 和 Bar 类:

#include "./foo.h"
#include <memory>

int main (int argc, char **argv)
{
  /**
   * Doesn't work
   */
  auto foo2 = std::make_shared<Foo<std::shared_ptr<Bar>>>;
  auto br = std::make_shared<Bar>;
  auto fut2 = std::async(std::launch::async, &Foo<std::shared_ptr<Bar>>::set_data, &foo2, bar);

  fut2.wait();
  std::cout << fut2.get()->data << std::endl;

  return 0;
}

我在编译 g++ -pthread test.cpp -o test 时遇到这个错误

test.cpp: In function ‘int main(int, char**)’:
test.cpp:20:94: error: no matching function for call to ‘async(std::launch, std::shared_ptr<Bar> (Foo<std::shared_ptr<Bar> >::*)(std::shared_ptr<Bar>), Foo<std::shared_ptr<Bar> >*, std::shared_ptr<Bar> (*&)())’
   auto fut2 = std::async(std::launch::async, &Foo<std::shared_ptr<Bar>>::set_data, &foo2, bar);
                                                                                              ^
In file included from ./foo.h:2:0,
                 from test.cpp:1:
/usr/include/c++/7/future:1712:5: note: candidate: template<class _Fn, class ... _Args> std::future<typename std::result_of<typename std::decay<_Tp>::type(typename std::decay<_Args>::type ...)>::type> std::async(std::launch, _Fn&&, _Args&& ...)
     async(launch __policy, _Fn&& __fn, _Args&&... __args)
     ^~~~~
/usr/include/c++/7/future:1712:5: note:   template argument deduction/substitution failed:
/usr/include/c++/7/future: In substitution of ‘template<class _Fn, class ... _Args> std::future<typename std::result_of<typename std::decay<_Tp>::type(typename std::decay<_Args>::type ...)>::type> std::async(std::launch, _Fn&&, _Args&& ...) [with _Fn = std::shared_ptr<Bar> (Foo<std::shared_ptr<Bar> >::*)(std::shared_ptr<Bar>); _Args = {Foo<std::shared_ptr<Bar> >*, std::shared_ptr<Bar> (*&)()}]’:
test.cpp:20:94:   required from here
/usr/include/c++/7/future:1712:5: error: no type named ‘type’ in ‘class std::result_of<std::shared_ptr<Bar> (Foo<std::shared_ptr<Bar> >::*(Foo<std::shared_ptr<Bar> >*, std::shared_ptr<Bar> (*)()))(std::shared_ptr<Bar>)>’
/usr/include/c++/7/future:1745:5: note: candidate: template<class _Fn, class ... _Args> std::future<typename std::result_of<typename std::decay<_Tp>::type(typename std::decay<_Args>::type ...)>::type> std::async(_Fn&&, _Args&& ...)
     async(_Fn&& __fn, _Args&&... __args)
     ^~~~~
/usr/include/c++/7/future:1745:5: note:   template argument deduction/substitution failed:
/usr/include/c++/7/future: In substitution of ‘template<class _Fn, class ... _Args> std::future<typename std::result_of<typename std::decay<_Tp>::type(typename std::decay<_Args>::type ...)>::type> std::async(_Fn&&, _Args&& ...) [with _Fn = std::launch; _Args = {std::shared_ptr<Bar> (Foo<std::shared_ptr<Bar> >::*)(std::shared_ptr<Bar>), Foo<std::shared_ptr<Bar> >*, std::shared_ptr<Bar> (*&)()}]’:
test.cpp:20:94:   required from here
/usr/include/c++/7/future:1745:5: error: no type named ‘type’ in ‘class std::result_of<std::launch(std::shared_ptr<Bar> (Foo<std::shared_ptr<Bar> >::*)(std::shared_ptr<Bar>), Foo<std::shared_ptr<Bar> >*, std::shared_ptr<Bar> (*)())>’

我认为这可能是因为引用 & 没有按照模板的所有尖括号的正确顺序工作,所以我尝试使用一些括号:

#include "./foo.h"
#include <memory>

int main (int argc, char **argv)
{
  /**
   * Doesn't work
   */
  Foo<std::shared_ptr<Bar>> foo2;
  Bar bar;
  auto fut2 = std::async(std::launch::async, &(Foo<std::shared_ptr<Bar>>::set_data), &foo2, bar);

  fut2.wait();
  std::cout << fut2.get().data << std::endl;

  return 0;
}

这会导致更短的错误,我也不明白。

test.cpp: In function ‘int main(int, char**)’:
test.cpp:20:75: error: invalid use of non-static member function ‘T Foo<T>::set_data(T) [with T = std::shared_ptr<Bar>]’
   auto fut2 = std::async(std::launch::async, &(Foo<std::shared_ptr<Bar>>::set_data), &foo2, bar);

我很困惑为什么共享指针会突然有所不同,我猜这与类型推导有关?感谢您的帮助。

编辑

感谢那些回复的人,这是解决方案。括号不是必需的,并且缺少一些 shared_ptr。

#include "./foo.h"
#include <memory>

int main (int argc, char **argv)
{
  Foo<std::shared_ptr<Bar>> foo2;
  auto bar = std::make_shared<Bar>(2.5);
  auto fut2 = std::async(std::launch::async, &Foo<std::shared_ptr<Bar>>::set_data), &foo2, bar;

  fut2.wait();
  std::cout << fut2.get()->data << std::endl;

  return 0;
}

最佳答案

有一些问题,我认为使用 lambda 可能会帮助您弄清楚发生了什么:

int main (int argc, char **argv)
{
  Foo<std::shared_ptr<Bar>> foo2;
  Bar bar;

  auto op = [foo2, bar]() mutable {return foo2.set_data(std::make_shared<Bar>(bar));};
  auto fut2 = std::async(std::launch::async, op);

  fut2.wait();
  std::cout << fut2.get()->data << std::endl;

  return 0;
}

你必须传递给 set_data一个shared_ptrFoo .其次,set_data不是 const 限定的,所以你需要一个可变的 lambda。 最后, future ,虽然返回时get() , 会给你一个 shared_ptrBar所以你需要运算符 -> . 您可以使代码更高效移动 Foo2Bar在 lambda 内部,但我试图让答案保持简单,特别是因为我不知道你是否想要在你的用例中重新使用 Foo2Bar , 但您可以考虑在 lambda 内部移动。

关于您的具体代码,以下是使用 C++14 在 g++ 9.1 中编译,参见 https://godbolt.org/z/DFZLtb

int main (int argc, char **argv)
{
  Foo<std::shared_ptr<Bar>> foo2;
  Bar bar;
  auto fut2 = std::async(std::launch::async, &Foo<std::shared_ptr<Bar>>::set_data, &foo2, std::make_shared<Bar>());

  fut2.wait();
  std::cout << fut2.get()->data << std::endl;

  return 0;
}

您需要提供一个 shared_ptr<Bar>作为参数而不是 Bar并且您需要删除 Foo<std::shared_ptr<Bar>>::set_data 周围的括号.

关于c++ - std::async 与共享指针模板化成员函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56952819/

相关文章:

c++ - 得到一个类的两个实例?

c++ - 更改 QCheckBox 文本颜色

c++ - GDB分步调试两个线程

c++ - ADL 在特定情况下不起作用

c++ - 有什么方法可以解决模板类中的前向声明?

c++ - 可以将两个由空格分隔的字符串传递给函数吗?需要解释

c++ - 虚拟继承和函数成员

java - 多线程和处理器的能力

c++ - 升压同步

javascript - 为什么监听器总是显示最后一次迭代的次数?