October 13th, 2026
intriguing1 reaction

C++ futures part 2: The mental model for std::promise, std::packaged_task

Last time, we looked at the “state” that powers the std::promise, std::packaged_task, std::async, std::future, and std::sharedfuture classes. Now we’ll see how that “state” (which I call the source) gets used and passed around.

The lifetime of the source is managed by a std::shared_ptr<source<T>>. Everybody who has a reference to the source uses this shared pointer.

The simplest case is std::promise. The operations on std::promise pretty much map directly to the operations on the source we laid out last time.

The next more complicated case is the std::packaged_task. This takes a callable and saves it for later. You can then call get_future to get a future and hand it to somebody as an IOU for “A value will appear here someday.”

When you want to produce a value, you invoke the packaged_task with whatever parameters you want to pass to the original callable. The result of the invocation (either a value or an exception) is then stored in the source object, which can then be retrieved by the future.

You can imagine a packaged task as just wrapping a promise:

template<typename R, typename... Args>
class packaged_task
{
    std::promise<R> p;
    std::function<R(Args...)> func;

public:
    template<typename F>
    packaged_task(F&& f) : func(std::forward<F>(f)) {}

    auto get_future() { return p.get_future(); }

    void operator()(Args... args) {
        try {
            p.set_value(func(args...));
        } catch (...) {
            p.set_exception(std::current_exception());
        }
    }
};

When you invoke it, it calls the stored callable with the arguments and puts the result in the promise.

Okay, those were the easy cases of pushing values into the source. Next time, we’ll look at the messy case: std::async.

Topics

Author

Raymond has been involved in the evolution of Windows for more than 30 years. In 2003, he began a Web site known as The Old New Thing which has grown in popularity far beyond his wildest imagination, a development which still gives him the heebie-jeebies. The Web site spawned a book, coincidentally also titled The Old New Thing (Addison Wesley 2007). He occasionally appears on the Windows Dev Docs Twitter account to tell stories which convey no useful information.

3 comments

Sort by :
  • LB 7 hours ago

    Something that annoys me with the design of packaged_task is that you can easily forget to take the future from it before sending it off to the races. Then later you check your future member variable and it has no state, and you assume it was already retrieved.

  • Christopher Warrington

    A small thing: looks like the catch block should be

    p.set_exception

    .