std::refReference (cppreference)std::reference_wrapperReference (cppreference)- ISO/IEC 14882 C++ Standard Section:
[refwrap]
std::reference_wrapper<T> is a class template that wraps a reference to an object or function of type std::ref and std::cref are helper factory functions that generate std::reference_wrapper<T> and std::reference_wrapper<const T> instances. Introduced in C++11 and enhanced with constexpr capabilities in C++20, these utilities overcome fundamental language limitations of native C++ references—enabling non-owning reference semantics inside STL containers (std::vector<std::reference_wrapper<T>>) and overriding forced value-decay in parameter-decaying templates (std::thread, std::bind, std::async).
Native C++ references (T&) are fundamental language aliases, but possess two strict design constraints:
- Non-Rebindable (Non-Assignable): A native reference must be initialized upon declaration and cannot be rebound to refer to another object. Writing
ref1 = obj2;mutates the value of the object currently bound toref1, rather than updatingref1to point toobj2. - Incompatible with Standard Library Containers: STL containers (
std::vector,std::list) require stored elements to be Erasable and CopyAssignable. Declaringstd::vector<int&>is illegal C++ syntax.
Template functions such as std::thread, std::bind, std::make_pair, and std::async apply std::decay to their template parameters, forcing arguments to be copied by value into internal state structures:
// C++11 Parameter Decay Conundrum
void worker(int& counter) { ++counter; }
int main() {
int val = 0;
// std::thread decay-copies 'val' into its thread context parameter buffer!
// std::thread t(worker, val); // COMPILE ERROR! Cannot pass copied 'int' to 'int&'!
}Solution: C++11 introduced std::reference_wrapper alongside std::ref and std::cref. Passing std::ref(val) creates a copyable value object holding val's address. Decaying templates copy the wrapper by value, allowing worker functions to dereference the wrapper and mutate the original variable val in caller scope.
Under the hood, std::reference_wrapper<T> encapsulates a non-null raw pointer T* m_ptr:
// Conceptual ISO Standard Implementation of std::reference_wrapper
template <class T>
class reference_wrapper {
private:
T* m_ptr; // Internally stores address of target object
public:
// Constructed from non-const lvalue reference
constexpr reference_wrapper(T& ref) noexcept : m_ptr(std::addressof(ref)) {}
// Deletes rvalue constructors to prevent dangling references to temporaries!
reference_wrapper(T&&) = delete;
// Copy Constructor & Assignment REBIND m_ptr to a new target object!
constexpr reference_wrapper(const reference_wrapper&) noexcept = default;
constexpr reference_wrapper& operator=(const reference_wrapper&) noexcept = default;
// Implicit conversion operator to native T&
constexpr operator T& () const noexcept { return *m_ptr; }
// Explicit accessor
constexpr T& get() const noexcept { return *m_ptr; }
};If target type std::reference_wrapper<T> overloads operator()(Args&&... args), executing std::invoke(*m_ptr, std::forward<Args>(args)...). This allows callable reference wrappers to act as lightweight, non-owning polymorphic function references.
To prevent immediate Dangling Reference Undefined Behavior, std::ref and std::cref explicitly delete rvalue overloads:
std::ref(42); // COMPILE ERROR! Deleted overload for rvalues!
std::ref(std::string("temp")); // COMPILE ERROR! Cannot bind reference_wrapper to temporary!┌───────────────────────────────────────────────────────────────────┐
│ C++11 │
├───────────────────────────────────────────────────────────────────┤
│ • Introduced std::reference_wrapper<T>, std::ref, and std::cref │
│ • Enabled non-owning reference storage in STL containers │
│ • Solved argument decay in std::thread and std::bind │
└───────────────────────────────────────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────────────┐
│ C++14 / C++17 │
├───────────────────────────────────────────────────────────────────┤
│ • Added std::unwrap_reference and std::unwrap_ref_decay traits │
│ • Class Template Argument Deduction (CTAD) for reference_wrapper │
└───────────────────────────────────────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────────────┐
│ C++20 │
├───────────────────────────────────────────────────────────────────┤
│ • Complete constexpr conversion for reference_wrapper and helpers │
│ • Interoperability with C++20 Ranges (std::views::all) │
└───────────────────────────────────────────────────────────────────┘
│
▼
┌───────────────────────────────────────────────────────────────────┐
│ C++23 / C++26 │
├───────────────────────────────────────────────────────────────────┤
│ • Refined constexpr evaluation in tuple decomposition & algorithms│
└───────────────────────────────────────────────────────────────────┘
Demonstrates using std::ref to pass mutable lvalue references into std::thread and std::async.
#include <iostream>
#include <thread>
#include <functional>
void increment_counter(int& counter, int iterations) {
for (int i = 0; i < iterations; ++i) {
++counter;
}
}
int main() {
int shared_counter = 0;
// std::thread decay-copies its arguments.
// Wrapping 'shared_counter' in std::ref() passes a copyable reference wrapper,
// allowing 'increment_counter' to mutate 'shared_counter' directly!
std::thread t(increment_counter, std::ref(shared_counter), 1000);
t.join();
std::cout << "Final Counter Value: " << shared_counter << " (Expected: 1000)\n";
return 0;
}Demonstrates storing polymorphic references in std::vector<std::reference_wrapper<T>> without dynamic heap memory allocation or smart pointer overhead.
#include <iostream>
#include <vector>
#include <functional>
#include <string>
struct Shape {
virtual ~Shape() = default;
virtual void draw() const = 0;
};
struct Circle : public Shape {
void draw() const override { std::cout << "Drawing Circle\n"; }
};
struct Square : public Shape {
void draw() const override { std::cout << "Drawing Square\n"; }
};
int main() {
Circle c;
Square s;
// std::vector<Shape&> is ILLEGAL syntax in C++!
// std::vector<std::reference_wrapper<Shape>> provides non-owning polymorphic reference storage:
std::vector<std::reference_wrapper<const Shape>> canvas;
canvas.push_back(std::cref(c)); // Store const reference to c
canvas.push_back(std::cref(s)); // Store const reference to s
std::cout << "Rendering Canvas Elements:\n";
for (const Shape& shape : canvas) {
shape.draw(); // Implicit conversion from reference_wrapper to Shape&!
}
return 0;
}Demonstrates wrapping callable closures and invoking them inside compile-time constant evaluation.
#include <iostream>
#include <functional>
constexpr int execute_callable(std::reference_wrapper<int(int, int)> func_ref, int a, int b) {
return func_ref(a, b); // Invokes underlying free function via overloaded operator()
}
constexpr int add_func(int x, int y) { return x + y; }
int main() {
// C++20: std::reference_wrapper is fully constexpr-capable!
constexpr auto ref_wrapper = std::ref(add_func);
constexpr int result = execute_callable(ref_wrapper, 20, 22);
static_assert(result == 42, "Compile-time reference wrapper call failed");
std::cout << "Compile-time callable reference result: " << result << "\n";
return 0;
}-
Beware of Dangling References (Use-After-Free):
std::reference_wrapperdoes not extend the lifetime of its target object. If the target object goes out of scope while thereference_wrapperremains, calling.get()results in Use-After-Free Undefined Behavior.
-
Use
std::vector<std::reference_wrapper<T>>for Non-Owning Collections:- Prefer
std::vector<std::reference_wrapper<T>>overstd::vector<T*>when building non-owning collections of existing objects to maintain clean reference syntax (obj.method()) and eliminate null pointer dereference checks.
- Prefer
-
Re-binding Reference Wrappers:
- Assigning one
reference_wrapperto another rebinds the pointer internal member (m_ptr). It does not invoke the assignment operator of the underlying target object.
- Assigning one
-
Related to:
-
std::thread/std::async— Primary APIs requiringstd::refto override parameter decay. -
std::unwrap_reference— Type trait extracting$T$ fromstd::reference_wrapper<T>. -
Lambda Expressions — Closures that can be passed by reference via
std::ref.
-
- Superseded by: None.
-
Supersedes: Raw pointers (
T*) used solely to simulate re-assignable reference wrappers in STL containers.
Back to C++11 section.