0

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.

  • 4
    Why in the name of Bjarne Stroustrup are you inheriting from `std::packaged_task`? Shouldn't you at best compose it? `std::packaged_task` is a resource that promises to produce something, You may want to see [Yakk's work](https://stackoverflow.com/documentation/c%2b%2b/2872/type-erasure/18042/a-move-only-stdfunction) – WhiZTiM Oct 12 '16 at 10:20
  • As stated in the question, I want to circumvent the requirement for a copy constructor in std::function. Composing makes me have to provide wrappers for all the functionality in `std::packaged_task` and doesn't get rid of the bug I'm encountering. – Spencer McLaughlin Oct 12 '16 at 10:31
  • As to why: I have a thread queue that you can post tasks to in the form of a std::function. I want to post non-void functions by wrapping them in a packaged_task, plop that on the queue and use the future from the packaged_task to get the result. – Spencer McLaughlin Oct 12 '16 at 10:40
  • Why are you creating a thread for a package_task? They run asynchronously by default. – amchacon Oct 12 '16 at 10:42
  • Are you thinking about std::async? packaged_task doesn't run asynchronously unless you do so manually. – Spencer McLaughlin Oct 12 '16 at 10:49
  • @WhiZTiM Thanks for the link. I've seen solutions based on creating a move only version of std::function but I'd rather not have to roll my own version of std::function unless I really have to. – Spencer McLaughlin Oct 12 '16 at 11:00

1 Answers1

1

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.

Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271