为啥 std::async 不能与接收抽象类引用作为参数的函数一起使用?

Posted

技术标签:

【中文标题】为啥 std::async 不能与接收抽象类引用作为参数的函数一起使用?【英文标题】:Why std::async cannot be used with functions that receive a reference to an abstract class as a parameter?为什么 std::async 不能与接收抽象类引用作为参数的函数一起使用? 【发布时间】:2019-07-07 18:23:10 【问题描述】:

我正在尝试通过std::async 执行接收参数的函数,该参数是对抽象类的引用,但似乎由于某种原因这无效。另一方面,如果我用指针替换提到的引用,一切正常。

为什么会这样?将抽象类参数作为指针传递通常会更好吗?

请看下面的例子:

std::async 使用不正确

#include <iostream>
#include <future>

class AbsClass 
  public:
    virtual int f() = 0;
;

class ImplClass : public AbsClass 
  public:
    int f() override  return 21; 
;

int func(AbsClass &asbclass) 
  return 210 + asbclass.f();


int main() 
  ImplClass ic;
  AbsClass &ac = ic;

  // This causes a compilation failure:
  std::future<int> res = std::async(&func, ac);

  std::cout << res.get() << std::endl;

显示失败

/usr/include/c++/7/future:1745:5: error: invalid abstract parameter type ‘AbsClass’
main.cpp:4:7: note:   because the following virtual functions are pure within ‘AbsClass’:
 class AbsClass 
       ^~~~~~~~
main.cpp:6:17: note:    virtual int AbsClass::f()
     virtual int f() = 0;

正确使用 std::async

#include <iostream>
#include <future>

class AbsClass 
  public:
    virtual int f() = 0;
;

class ImplClass : public AbsClass 
  public:
    int f() override  return 21; 
;

int func(AbsClass *asbclass) 
  return 210 + asbclass->f();


int main() 
  ImplClass ic;
  AbsClass &ac = ic;

  std::future<int> res = std::async(&func, &ac);

  std::cout << res.get() << std::endl;

【问题讨论】:

【参考方案1】:

需要存储参数,这意味着它们被复制。并且引用不能被复制。

因此引入了reference wrapper,它可以存储引用同时也可以被复制。您可以将其与辅助函数 std::ref and std::cref 一起使用:

std::future<int> res = std::async(&func, std::ref(ac));  // Pass ac by reference

【讨论】:

值得注意的是,由于无法实例化抽象类,因此复制失败。如果它不是一个抽象类,程序会编译,但你会面临Object Slicing同样不吸引人的问题。 有趣的旁注:指针也会发生完全相同的事情。指针被复制,但由于您不关心是否有原始指针(尽管有时您有),所以您不会注意到副本。 如何知道参数需要存储?我无法在 std::async 文档中阅读。当你说引用不能被复制时,你是什么意思?我既可以复制引用指向的地址,也可以复制它指向的实际值,对吧? @Dan 未指定何时调用该函数。可以立即调用它,也可以在稍后的任何时间点调用 get 返回的未来。因此必须存储函数的参数。 @Dan async 没有在这个话题上说太多,但async 使用了threadthread 对此进行了警告。以下是 CPP 参考中the notes 针对std::thread 的构造函数的简要讨论: 线程函数的参数按值移动或复制。如果需要将引用参数传递给线程函数,则必须对其进行包装(例如,使用std::refstd::cref

以上是关于为啥 std::async 不能与接收抽象类引用作为参数的函数一起使用?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 std::future 从 std::packaged_task 和 std::async 返回不同?

为啥不能在 PHP 中从抽象类中调用抽象函数?

为啥你不能在 Java 中有一个受保护的抽象类?

C++并发与多线程 11_std::atomic叙谈std::launch(std::async) 深入

std::async的使用总结

接口和抽象类的相同与区别