3

I'm trying to execute a function that receives a parameter, which is a reference to an abstract class, through std::async but it seems that that is not valid for some reason. On the other hand, if I replace the mentioned reference by a pointer everything works.

Why does this happen? Is it generally better to pass abstract class parameters as pointers?

See the examples below:

Incorrect use of 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;
}

Failure displayed

/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;

Correct use of 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;
}
Dan
  • 2,452
  • 20
  • 45

1 Answers1

7

The arguments needs to be stored, which means they are copied. And references can't be copied.

Therefore a reference wrapper was introduced, that can store references while also being able to be copied. You can use it with the helper function std::ref and std::cref:

std::future<int> res = std::async(&func, std::ref(ac));  // Pass ac by reference
Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
  • 1
    Worth noting the copies fail because the abstract class cannot be instantiated. If it was not an abstract class, the program would compile but you would face the equally unappealing problem of [Object Slicing](https://stackoverflow.com/questions/274626/what-is-object-slicing). – user4581301 Jul 07 '19 at 18:37
  • 2
    Fun side note: The exact same thing happens with the pointer. The pointer is copied, but since you don't care about whether or not you have the original pointer (though sometimes you do) you don't notice the copy. – user4581301 Jul 07 '19 at 18:40
  • How do you know that the arguments needs to be stored? I can't read that in the std::async doc. What do you mean when you say that references can't be copied? I can both copy the address to which the reference is pointing and copy the actual value to which it is pointing, right? – Dan Jul 07 '19 at 19:47
  • @Dan It's not specified *when* the function will be called. It can be called immediately, or at any later point in time up to you call `get` on the returned future. Therefore the arguments to the function have to be stored. – Some programmer dude Jul 07 '19 at 19:53
  • @Dan `async` doesn't say much on the topic, but `async` uses `thread` and `thread` warns of this. Here's a brief discussion in [the notes](https://en.cppreference.com/w/cpp/thread/thread/thread#Notes) for `std::thread`'s constructor from CPP Reference: *The arguments to the thread function are moved or copied by value. If a reference argument needs to be passed to the thread function, it has to be wrapped (e.g. with [`std::ref`](https://en.cppreference.com/w/cpp/utility/functional/ref) or [`std::cref`](https://en.cppreference.com/w/cpp/utility/functional/ref).* – user4581301 Jul 08 '19 at 01:44