c++functionc++11standardsc++26

Is `std::function` deprecated by `std::copyable_function` in C++26?


C++26 provides std::copyable_function [cppref link]. However, the existing std::function is already copyable. So, I have 3 questions:

  1. What are the key advantages of std::copyable_function over std::function?

  2. Invoking an empty std::copyable_function is undefined, while invoking an empty std::function will throw an exception. What's the rationale behind?

  3. Is std::function deprecated by std::copyable_function in C++26?


Solution

  • What are the key advantages of std::copyable_function over std::function?

    There are two.

    First, there's no target() API, which generally wasn't super useful anyway, so there's less overhead because std::copyable_function doesn't have to track something that std::function did. But that's minor.

    The more important one is the API surface. std::function looks like this:

    struct function<R(Args...)> {
        R operator()(Args...) const;
    };
    

    The call operator (1) always const and (2) never noexcept. (1) is a problem because you can still invoke a non-const member function without you intending to.

    std::copyable_function looks like this:

    struct copyable_function<R(Args...) cv ref noexc> {
      R operator()(Args...) cv ref noexc;
    };
    

    Meaning that, for instance, copyable_function<void(int)> has a non-const, non-noexcept call operator, but copyable_function<void(int) const noexcept> has one that is both const and noexcept. All of this is specifiable.

    This makes the usage clear and descriptive... and more correct!

    Invoking an empty std::copyable_function is undefined, while invoking an empty std::function will throw an exception. What's the rationale behind?

    Arguably, invoking an empty std::function is a bug. There are many people who think that throwing to signal programmer error is a bad way to do so - calling it undefined behavior means the implementation can assert.

    Is std::function deprecated by std::copyable_function in C++26?

    The literal answer to this question is: no. But maybe the more interesting question is: should it have been? To which I would still answer no.

    There's a very large amount of code that uses std::function and a large percentage of it is likely correct. It's also not a simple renaming to transition either, since many uses will want to change from function<R(Args...)> to copyable_function<R(Args...) const>. But also many uses will want to change to move_only_function<R(Args...) const> instead.

    It's probably a good idea to actually make those changes (especially if you don't need target(), which ... nobody does). But they're not so pressing that you actually want a [[deprecated]] warning out of it. Seems more like a low-key clang-tidy modernization hint kind of suggestion.

    There's really two levels here:

    1. A is bad (possibly actively harmful), B is strictly superior, please switch.
    2. A is fine, B is better. Prefer B in new code, but A isn't harmful.

    Transitioning from auto_ptr<T> to unique_ptr<T> fits into that first category. Transitioning from function<R(Args..)> to copyable_function<R(Args...) const> (or move_only_function<R(Args...) const>, or sometimes even function_ref<R(Args...) const>) to me is more like the second category. A is fine. Don't yell at me for still using it in older code.