How std::reference_wrapper Works in C++
This article provides an overview of how the C++ standard library
implements reference wrapper mechanics via
std::reference_wrapper. Introduced in C++11, this utility
bridges the gap between reference semantics and value semantics,
allowing developers to store references in standard containers and pass
them through templates that normally decay types to values. Below is an
explanation of its internal mechanics, key member functions, and
practical applications.
The Problem with Native References
In C++, native references (T&) are not objects. They
lack storage identity, cannot be reseated once initialized, and do not
possess assignment semantics that overwrite the reference itself
(assignment modifies the referent instead). Because of these
characteristics, standard containers like
std::vector<T&> are ill-formed; standard library
containers require elements to be Erasable, CopyAssignable, and
MoveAssignable.
Internal Mechanics
of std::reference_wrapper
Defined in the <functional> header,
std::reference_wrapper<T> is a class template that
wraps a reference in a copyable, assignable object. It emulates a
reference while exhibiting the mechanics of a first-class object.
Internally, std::reference_wrapper<T> functions
essentially as a non-null, copyable pointer:
- Internal Storage: The class typically stores an
internal pointer to the referenced object:
T* ptr;. - Construction: It accepts an lvalue reference of
type
T&upon construction and stores its address (ptr = std::addressof(val)). Rvalue references are explicitly disallowed; attempting to construct astd::reference_wrapperfrom a temporary triggers a deleted constructor to prevent dangling references. - Reseatability: Unlike a native reference, copying
or assigning a
std::reference_wrappercopies the internal pointer, not the underlying object. This allows the wrapper to be reseated to point to a different object during its lifetime.
Type Conversion and Access
std::reference_wrapper<T> provides mechanisms to
access the underlying reference transparently:
- Implicit Conversion Operator: It defines an
operator T&() const noexceptthat implicitly converts the wrapper back to an lvalue reference whenever an expression expectsT&. - Explicit Access: The
.get()member function returnsT&directly, which is useful when template deduction cannot infer the implicit conversion.
Callable Object Invocation
If the underlying type T is a callable entity (such as a
function pointer, lambda, or class with operator()),
std::reference_wrapper<T> defines an overloaded call
operator (operator()). It uses std::invoke to
forward any arguments to the stored callable, returning the result. This
enables instances of std::reference_wrapper to act as
drop-in function objects without copying large or stateful callable
types.
Helper Functions:
std::ref and std::cref
To avoid manual template instantiation, the standard library provides helper functions:
std::ref(val): Deduces the type and returnsstd::reference_wrapper<T>(val).std::cref(val): Returnsstd::reference_wrapper<const T>(val).
These helpers are critical when passing arguments to template
utilities such as std::bind, std::make_pair,
or std::thread, which otherwise decay reference types and
make copies of the passed arguments.
Primary Use Cases
Storing References in Containers: A
std::vector<std::reference_wrapper<T>>allows the storage of non-owning references inside a standard dynamic array, offering polymorphic or aliased access without heap allocation or pointer syntax.Passing by Reference to Value-Decaying Templates: Utilities like
std::threadcopy their arguments by default. Wrapping an argument withstd::refsignals to the forwarding machinery that the target function should receive an actual reference rather than a decayed copy.