c++c++11packaged-task

Adding dummy copy constructor to std::packaged_task


I'm trying to bypass std::packaged_task's lack of a copy constructor so that I can pass it into a std::function (which will only ever be moved).

I inherited from std::packaged_task and added a dummy copy constructor, which I assume shouldn't be called if I never copy the std::function it's moved into.

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

template <typename T>
class MyPackagedTask : public std::packaged_task<T()> {
  public:
    template <typename F>
    explicit MyPackagedTask(F&& f)
      : std::packaged_task<T()>(std::forward<F>(f)) {}

    MyPackagedTask(MyPackagedTask&& other)
      : std::packaged_task<T()>(std::move(other)) {}

    MyPackagedTask(const MyPackagedTask& other) {
      // Adding this borks the compile
    }
};

int main()
{
    MyPackagedTask<int> task([]() {return 0;});
    auto future = task.get_future();
    std::thread t(std::move(task));
    t.join();

    std::cout << future.get() << std::endl;
}

Compiling this with gcc 6.2.1 I get the following error message (just the end part, let me know if you want the whole thing...):

/usr/include/c++/6.2.1/future:1325:6: error: invalid use of void expression
      (*_M_result)->_M_set((*_M_fn)());

The error message was unparseable to me, so I was wondering if I'm doing something wrong or if the compiler is failing.


Solution

  • The move constructor definition is not correct, it forwards std::packaged_task<T()>&& as MyPackagedTask&& and that invokes a wrong constructor of std::packaged_task<T()>. This is the cause of the compiler error you observe.

    The correct one:

    MyPackagedTask(MyPackagedTask&& other)
      : std::packaged_task<T()>(static_cast<std::packaged_task<T()>&&>(other))
    {}
    

    In there static_cast<std::packaged_task<T()>&&>(other) does both the upcast to the base class and std::move.