std :: async与共享指针模板成员函数

时间:2019-07-09 12:39:40

标签: 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_ptrs。

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

4 个答案:

答案 0 :(得分:2)

有一些问题,我认为使用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_datashared_ptr传递到Foo。其次,set_data不是const限定的,因此您需要一个可变的lambda。 最后,将来通过get()返回时,将给您shared_ptrBar,因此您需要运算符->。 您可以使代码在Lambda内移动Foo2Bar时更高效,但是我试图使答案保持简单,特别是因为我不知道是否要在用例中重新创建代码-使用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周围的括号。

答案 1 :(得分:2)

您的代码中只包含一些输入错误,在第一个shared_ptr示例中,您将shared_ptr<Foo>而不是原始ptr Foo*传递给函数。在第二个示例中,第一个参数Foo*是正确的,但是第二个参数是Bar而不是shared_ptr<Bar>。这里有工作示例:

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 <future>

TEST(xxx, yyy) {
    Foo<std::shared_ptr<Bar> > foo;
    auto bar = std::make_shared<Bar>();
    bar->data = 42;
    auto futureResult = std::async(std::launch::async,
        &Foo<std::shared_ptr<Bar> >::set_data, &foo, bar);
    std::cout << futureResult.get()->data << std::endl;
}

请注意,如果您需要对Foo类内部的某些数据运行异步操作,则最好提供在类内部执行该异步操作并返回将来的接口。

std::future<T> runAsyncOper(const T& data);

答案 2 :(得分:0)

您的第一个带有共享指针的示例没有将共享指针作为最后一个参数传递给std :: async,而是将指针传递给了函数(您没有添加括号)

int main()
{
    /**
    * Works :)
    */
    Foo<std::shared_ptr<Bar>> foo2;

    auto br = std::make_shared<Bar>();

    auto fut2 = std::async(std::launch::async, &Foo<std::shared_ptr<Bar>>::set_data, &foo2, br);

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

    return 0;
}

答案 3 :(得分:0)

问题是您将错误类型的参数传递给std::async,并错误地使用了std::make_shared

对代码的最小修改:

  auto foo2 = std::make_shared<Foo<std::shared_ptr<Bar>>>();
  auto bar = 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;

https://wandbox.org/permlink/3YXG56ahFKrZs8GB