std::copyable_functionReference (cppreference)- ISO/IEC 14882 C++ Standard Section:
[func.wrap.copy]
std::copyable_function is a polymorphic, type-erased, copyable callable wrapper template in <functional> introduced in C++26 (via proposal P2548R6). Designed to resolve the long-standing architectural flaws of C++11's std::function (which lacked support for const, noexcept, and ref-qualified call signatures, and violated strict const-correctness), std::copyable_function provides a copyable, value-owning function container that supports full signature qualification specifiers (const, noexcept, &, &&). It is the primary abstraction for multi-subscriber event buses, broadcast logging pipelines, and GUI callback registries requiring copyable function handles with strict static type guarantees.
C++11 introduced std::function<R(Args...)> as a copyable polymorphic callable container. However, as C++ evolved, std::function suffered from two severe structural defects:
Calling a const instance of std::function allowed mutating the internal state of a non-const captured target lambda (e.g. mutable lambdas). The operator() const member function of std::function invoked a non-const operator() on its internal target object, violating the fundamental C++ invariant that const member methods must not mutate non-mutable object state!
// C++11/17 std::function Const-Correctness Flaw
int counter = 0;
auto mutable_lambda = [counter]() mutable { return ++counter; };
const std::function<int()> const_wrapper = mutable_lambda;
// C++17 DISASTER: Calling operator() const on const_wrapper MUTATES internal state!
const_wrapper(); // Compiles and mutates state despite const_wrapper being CONST!std::function supported only unqualified signatures R(Args...). It could not declare that a callback was non-throwing (noexcept), or that a callback expected lvalue (&) vs. rvalue (&&) invocation.
C++23 introduced std::move_only_function, which added full const/noexcept/ref-qualifier signature support, but was strictly move-only (non-copyable). Multi-listener event dispatchers, GUI signal handlers, and broadcast channels requiring copyable function handles with noexcept or const guarantees had no standard solution.
Solution: ISO C++26 proposal P2548R6 introduced std::copyable_function, combining copyable value ownership with full signature qualifications (const, noexcept, &, &&) and strict const-correctness.
// 1. Const-qualified invocation: Requires stored target to be const-invocable!
std::copyable_function<void() const> f1;
// 2. Non-throwing invocation: Enforces that target's operator() is noexcept!
std::copyable_function<void() noexcept> f2;
// 3. Lvalue Ref-Qualified invocation: Callable only when wrapper is an lvalue
std::copyable_function<void() &> f3; Standard Function Wrappers
│
┌──────────────────────────────────┼──────────────────────────────────┐
▼ ▼ ▼
1. std::function<R(Args...)> 2. std::move_only_function (C++23) 3. std::copyable_function (C++26)
• Copyable • Move-Only • Copyable
• NO const/noexcept qualifications • Full const/noexcept/&/&& support • Full const/noexcept/&/&& support
• Const-correctness violation bug! • Small Buffer Optimization • Fixes const-correctness bug!
| Wrapper Class | C++ Standard | Copyable? | Accepts Move-Only Targets? | Supports const / noexcept / & / &&? |
Enforces Strict Const-Correctness? |
|---|---|---|---|---|---|
std::function |
C++11 | YES | NO | NO | NO (Violates const) |
std::move_only_function |
C++23 | NO | YES (std::unique_ptr OK) |
YES | YES |
std::copyable_function |
C++26 | YES | NO (Target must be copyable) | YES | YES |
std::function_ref |
C++26 | YES (Non-owning) | YES (Borrowing view) | YES | YES |
┌──────────────────────────────────────────────────────────────────┐
│ C++11 / C++14 / C++17 / C++20 │
├──────────────────────────────────────────────────────────────────┤
│ • std::function was the sole copyable function wrapper │
│ • Lacked noexcept, const, and ref-qualifier signature types │
│ • Violated const-correctness when invoking const wrappers │
└──────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────┐
│ C++23 │
├──────────────────────────────────────────────────────────────────┤
│ • Introduced std::move_only_function (P1651R2 / P0288R9) │
│ • Supported qualifications, but was strictly NON-COPYABLE │
└──────────────────────────────────────────────────────────────────┘
│
▼
┌──────────────────────────────────────────────────────────────────┐
│ C++26 │
├──────────────────────────────────────────────────────────────────┤
│ • Introduced std::copyable_function in <functional> (P2548R6) │
│ • Copyable ownership + full const/noexcept/ref-qualifier support │
│ • Fixed const-correctness invocation bugs │
└──────────────────────────────────────────────────────────────────┘
Demonstrates storing multiple copies of a callback handle inside a broadcast event registry.
#include <iostream>
#include <vector>
// C++26 <functional> Header
#if defined(__cpp_lib_copyable_function) && __cpp_lib_copyable_function >= 202306L
#include <functional>
class EventBroadcaster {
private:
// C++26 std::copyable_function handles can be COPIED into multiple subscriber slots!
std::vector<std::copyable_function<void(int) const>> m_listeners;
public:
void subscribe(std::copyable_function<void(int) const> listener) {
m_listeners.push_back(listener); // Copy 1 into vector
m_listeners.push_back(std::move(listener)); // Copy 2 into vector
}
void dispatch(int event_id) const {
for (const auto& listener : m_listeners) {
listener(event_id); // Invokes const-qualified copyable function
}
}
};
int main() {
EventBroadcaster bus;
auto logger = [](int id) {
std::cout << " [Subscriber Log] Event " << id << " received.\n";
};
bus.subscribe(logger);
bus.dispatch(1001);
return 0;
}
#else
int main() {
std::cout << "C++26 std::copyable_function demonstrated.\n";
return 0;
}
#endifDemonstrates how std::copyable_function<void() noexcept> enforces non-throwing exception contracts at compile time.
#include <iostream>
#if defined(__cpp_lib_copyable_function) && __cpp_lib_copyable_function >= 202306L
#include <functional>
void safe_telemetry_job() noexcept {
std::cout << "Executing strictly non-throwing telemetry callback.\n";
}
void throwing_job() {
throw std::runtime_error("Error!");
}
int main() {
// 1. Valid: safe_telemetry_job is declared noexcept!
std::copyable_function<void() noexcept> safe_task = safe_telemetry_job;
safe_task();
// 2. UNCOMMENTING BELOW FAILS AT COMPILE TIME (Target is NOT noexcept!):
// std::copyable_function<void() noexcept> bad_task = throwing_job;
// COMPILER ERROR: Target callable does not satisfy 'noexcept' specification!
return 0;
}
#else
int main() { return 0; }
#endifDemonstrates std::copyable_function rejecting non-const mutable closures when qualified as const.
#include <iostream>
#if defined(__cpp_lib_copyable_function) && __cpp_lib_copyable_function >= 202306L
#include <functional>
int main() {
int state = 0;
auto mutable_closure = [state]() mutable {
return ++state; // Mutates local copy inside closure!
};
// C++26 std::copyable_function<int() const> REQUIRES the callable to be CONST-INVOCABLE!
// UNCOMMENTING BELOW FAILS AT COMPILE TIME (Fixes C++11 std::function const bug!):
// std::copyable_function<int() const> const_fn = mutable_closure;
// COMPILER ERROR: Cannot bind mutable closure to const-qualified copyable_function!
// Correct Usage: Use non-const signature for mutable closures
std::copyable_function<int()> non_const_fn = mutable_closure;
std::cout << "Mutable Closure Step 1: " << non_const_fn() << " (1)\n";
return 0;
}
#else
int main() { return 0; }
#endif-
Use
std::copyable_functionfor Multi-Subscriber Event Registries:- Standardize on
std::copyable_functionwhen building broadcast event dispatchers, GUI action listeners, or multi-threaded worker pools where callbacks must be copied across multiple handler slots while enforcingnoexceptorconstguarantees.
- Standardize on
-
std::copyable_functionRequiresCopyConstructibleTargets:std::copyable_functioncannot store move-only callables (such as lambdas capturingstd::unique_ptr).- Rule: Use
std::move_only_function(C++23) for move-only task queues, andstd::copyable_function(C++26) when callbacks must be copyable.
-
Check Validity (
operator bool()) Before Calling:- Calling an unassigned
std::copyable_functionthrowsstd::bad_copyable_function_call(or causes UB if qualified asnoexcept). Always verifyif (fn)before invocation.
- Calling an unassigned
- Related to:
- std::function — Predecessor copyable wrapper.
- std::move_only_function — Move-only variant for unique resource closures.
std::function_ref(C++26) — Non-owning zero-allocation callback view.- Ref-Qualified Member Functions — Underpins
&/&&qualification syntax.
- Superseded by: None.
- Supersedes:
std::functionfor modern C++26 codebases due to strictconst-correctness andnoexceptqualification support.
Back to C++26 section.