Reference Wrapper
std::reference_wrapper, std::ref and std::cref: copyable, assignable reference semantics for containers, bind, threads and generic callables.
std::reference_wrappersince C++11A copyable, assignable class template in <functional> that stores a reference to an object or function of type T and converts implicitly back to T&, so references can pass through code that would otherwise copy its arguments.
Overview
A native reference cannot be rebound, cannot be stored in a standard container, and decays to a copy when passed to templates that take parameters by value. std::reference_wrapper<T> (C++11) fixes this. Internally it holds a pointer, but it presents reference-like semantics: copying it copies the binding (not the referent), and assigning it rebinds it.
Key properties:
- Implicit conversion to
T&(operator T&()), plus explicit access viaget(). - Rebindable:
a = bon two wrappers makesarefer tob's target. It never assigns through to the referent. - Trivially copyable (guaranteed since C++20 for the copy operations being trivial in practice; treat it as cheap, pointer-sized).
- Callable: if
Tis callable,operator()forwards to it, with the return type computed viastd::invoke_result(C++17 semantics; in C++11/14 viaresult_of). - Factories:
std::ref(x)andstd::cref(x)(C++11) deduceTandconst T. Passing an existingreference_wrappertorefreturns it rather than nesting wrappers. - Binding to rvalues is deleted:
std::ref(std::string{"tmp"})does not compile.crefof an rvalue is also deleted (const T&&overloads are deleted since C++11).
Changes by standard:
- C++14:
ref/crefgained theconstexprspecifier on their overloads... in practice, they are constexpr from C++20 (reference_wrappermembers andref/crefare constexpr since C++20). - C++20: all members and
std::ref/std::crefareconstexpr. - C++26: adds a
reference_wrappercomparison (operator==, and with it<=>for the wrapped type) when the underlying type supports it; before C++26 wrappers are not comparable unless converted throughget()or implicit conversion. - C++26: also adds the ability to construct wrappers from a
T&without ambiguity for types that themselves are convertible toreference_wrapper(a constraint tweak).
Syntax
#include <functional>
int x = 0;
std::reference_wrapper<int> r = std::ref(x); // C++11
std::reference_wrapper<const int> c = std::cref(x); // C++11
int& a = r; // implicit conversion
int& b = r.get(); // explicit access
r.get() = 5; // modifies x
// Callable wrapper (C++11)
void hello(int);
std::reference_wrapper<void(int)> fw = std::ref(hello);
fw(42);
// CTAD (C++17)
std::reference_wrapper rr = std::ref(x); // deduces reference_wrapper<int>Member type (the wrapped type) is available as reference_wrapper<T>::type (C++11), with result_type and argument_type only for function types (deprecated in C++17, removed in C++20).
Examples
Containers of references (C++11)
#include <functional>
#include <vector>
#include <algorithm>
#include <iostream>
struct Widget { int id; };
int main() {
Widget a{3}, b{1}, c{2};
std::vector<std::reference_wrapper<Widget>> view{a, b, c}; // C++11
// Sort the view without moving the widgets themselves
std::sort(view.begin(), view.end(),
[](const Widget& l, const Widget& r) { return l.id < r.id; });
view[0].get().id = 100; // modifies the original (b)
std::cout << b.id << '\n'; // 100
}The lambda parameters take const Widget&, so the implicit conversion applies. Note that view[0].id would not compile: member access does not pass through the wrapper, which is why .get() is needed.
Threads and std::bind (C++11)
std::thread, std::async and std::bind decay-copy their arguments. Without ref, a function taking T& will not compile (thread) or will silently mutate a copy (bind).
#include <functional>
#include <thread>
#include <vector>
void fill(std::vector<int>& out, int value) { out.push_back(value); }
int main() {
std::vector<int> data;
std::thread t(fill, std::ref(data), 7); // C++11: reference passed through
t.join(); // data == {7}
auto bound = std::bind(fill, std::ref(data), std::placeholders::_1); // C++11
bound(8); // data == {7, 8}
}Lifetime is your responsibility: the referent must outlive the thread or bound object. Concurrent access still requires synchronization.
Avoiding copies of stateful callables (C++11)
#include <algorithm>
#include <functional>
#include <vector>
struct Counter {
int calls = 0;
bool operator()(int v) { ++calls; return v % 2 == 0; }
};
int main() {
std::vector<int> v{1, 2, 3, 4, 5, 6};
Counter pred;
// Most algorithms may copy the predicate; wrapping preserves the state
auto n = std::count_if(v.begin(), v.end(), std::ref(pred));
// n == 3, pred.calls == 6
}Unlike std::function, which stores a copy of the callable, a wrapper stores a reference. This is also the standard way to keep a non-copyable or expensive-to-copy functor out of a std::function: std::function<bool(int)> f = std::ref(pred); (C++11) performs no allocation for the wrapper.
Tuples, pairs and optional
#include <functional>
#include <optional>
#include <tuple>
#include <string>
int main() {
int i = 0; double d = 0; std::string s;
// Unpack a tuple into existing variables (C++11)
std::make_tuple(std::ref(i), std::ref(d), std::ref(s)) =
std::make_tuple(1, 2.5, std::string("hi"));
// Prefer std::tie(i, d, s) for this exact purpose
auto p = std::make_pair(std::ref(i), std::ref(i)); // pair<int&, int&>
++p.first; ++p.second; // i == 3
// std::optional<T&> is ill-formed (C++17); use a wrapper
std::optional<std::reference_wrapper<std::string>> maybe = std::ref(s); // C++17
if (maybe) maybe->get() += "!";
}std::invoke (C++17) and std::mem_fn/std::bind unwrap a reference_wrapper when it is supplied as the object for a pointer-to-member call, so std::invoke(&Widget::id, std::ref(w)) works.
Seeding and sharing random engines (C++11)
#include <algorithm>
#include <functional>
#include <random>
#include <array>
int main() {
std::random_device rd;
std::array<unsigned, 8> seed_data;
std::generate(seed_data.begin(), seed_data.end(), std::ref(rd)); // no copy of rd
}Algorithms such as std::generate and std::shuffle take engines or generators by value or forwarding reference; wrapping an engine ensures its state advances in the caller.
Best Practices
- Use
std::ref/std::crefat API boundaries that copy (threads,bind,async,make_tuple,make_pair), not inside general code where a plain reference works. - Prefer a lambda capture by reference (
[&x], C++11) overstd::bind(f, std::ref(x), _1); lambdas are clearer and often better optimized. Lambdas are preferred overbindsince C++14. - Use
std::crefwhen the callee reads only, so a non-const object can bind to aconst T¶meter without accidental mutation. - Use
reference_wrapperas the element type for "views" into another container when you need to reorder or filter without copying elements. For a non-owning, nullable, rebindable handle, a raw pointer is often equally good. - Call
.get()explicitly when deducing withautoor calling templates; implicit conversion does not happen during template deduction.
Common Pitfalls
- Dangling references: the wrapper does not extend lifetime. A wrapper in a detached thread or stored
std::functionoutliving its target is undefined behavior. - Rvalues are rejected:
std::ref(make_string())is a deleted overload by design, preventing a guaranteed dangling wrapper. - No member access:
w.memberandw->memberdo not work onreference_wrapper<T>. Usew.get().member. - Deduction with
auto:auto x = std::ref(i);makesxareference_wrapper<int>, notint&. Arithmetic still works through conversion (x + 1), butx = 5does not assign toi; it fails to compile forint, and for class types it may rebind rather than assign. - Assignment semantics differ from references:
w1 = w2rebinds. To assign through, writew1.get() = w2.get();. - Overload resolution surprises:
std::cout << std::ref(i)works via conversion for fundamental types, but templates such asstd::format(C++20) or user-defined generic printers that takeTby deduction will seereference_wrapper<int>and may fail to find a formatter. - Comparisons: before C++26,
reference_wrapper<T>has nooperator==; comparisons work only through implicit conversion whenThas a non-template comparison, and fail in containers needing deduced operators (for examplestd::findon a vector of wrappers with a plainTvalue usually works through conversion, butstd::setof wrappers needs a custom comparator). - Thread safety: wrapping a shared object into several threads shares it; you must provide a mutex or use
std::atomicfor the data. std::unwrap_reference/std::unwrap_ref_decay(C++20): generic code that wants to treat wrappers as references (asmake_pairandmake_tupledo) can use these traits to obtainT&fromreference_wrapper<T>.
See Also
- std::any: type-erased value storage; holds copies, so use
reference_wrapperinside to store references - std::atomic: synchronization when a reference is shared across threads
std::function,std::bind,std::invoke,std::tie,std::optionalfor related functional and vocabulary types