skip to content
Mehdi Mehdikhani
Table of Contents

std::ref creates reference wrappers that let you store references in containers or pass them to functions that normally make copies. It’s not used often, but it’s handy when you need it.

What std::ref Actually Does

std::ref wraps a reference in an object that can be copied and stored:

#include <functional>
int x = 42;
auto ref_wrapper = std::ref(x); // Creates std::reference_wrapper<int>
ref_wrapper = 100; // Modifies x through the wrapper
std::cout << x << std::endl; // Prints 100

The wrapper acts like a reference but can be copied, assigned, and stored in containers.

Why You Need Reference Wrappers

Regular references can’t be stored in containers or reassigned:

int a = 1, b = 2, c = 3;
// This doesn't work - can't store references in vector
// std::vector<int&> refs = {a, b, c}; // Compilation error
// This works - store reference wrappers instead
std::vector<std::reference_wrapper<int>> refs = {std::ref(a), std::ref(b), std::ref(c)};
// Modify through the wrappers
for (auto& ref : refs) {
ref.get() *= 2; // .get() returns the actual reference
}
// a=2, b=4, c=6

When I Actually Use std::ref

Most of the time, I use std::ref for:

  1. Algorithms that normally copy arguments:
void increment(int& value) {
value++;
}
std::vector<int> numbers = {1, 2, 3, 4, 5};
// This doesn't work - for_each passes copies to increment
// std::for_each(numbers.begin(), numbers.end(), increment);
// This works - passes the function by reference
std::for_each(numbers.begin(), numbers.end(), std::ref(increment));
// Or more commonly with lambdas:
int counter = 0;
std::for_each(numbers.begin(), numbers.end(), [&counter](int n) {
counter += n; // Capture by reference works fine here
});
  1. Threading with references to existing variables:
void worker_function(int& shared_data) {
for (int i = 0; i < 1000; ++i) {
shared_data++;
}
}
int data = 0;
// This doesn't work - std::thread copies arguments by default
// std::thread t(worker_function, data); // Passes copy of data
// This works - explicitly pass by reference
std::thread t(worker_function, std::ref(data));
t.join();
std::cout << data << std::endl; // Should be 1000
  1. Storing references in data structures:
class Observer {
public:
virtual void notify(const std::string& message) = 0;
};
class EventSystem {
std::vector<std::reference_wrapper<Observer>> observers_;
public:
void add_observer(Observer& observer) {
observers_.push_back(std::ref(observer));
}
void notify_all(const std::string& message) {
for (auto& observer_ref : observers_) {
observer_ref.get().notify(message); // Call through reference
}
}
};
class LogObserver : public Observer {
public:
void notify(const std::string& message) override {
std::cout << "Log: " << message << std::endl;
}
};
// Usage
EventSystem events;
LogObserver logger;
events.add_observer(logger); // Store reference to existing object
events.notify_all("Something happened");
  1. Function binding with references:
void process_data(const std::string& data, int& result_counter, double& result_sum) {
result_counter++;
result_sum += data.length();
}
int count = 0;
double sum = 0.0;
// Bind function with references to existing variables
auto bound_processor = std::bind(process_data,
std::placeholders::_1, // data parameter
std::ref(count), // reference to count
std::ref(sum)); // reference to sum
std::vector<std::string> data = {"hello", "world", "test"};
std::for_each(data.begin(), data.end(), bound_processor);
std::cout << "Processed " << count << " items, total length: " << sum << std::endl;

std::cref for Const References

std::cref creates const reference wrappers:

void print_value(const int& value) {
std::cout << "Value: " << value << std::endl;
}
const int readonly_data = 42;
// For const references, use std::cref
std::thread t(print_value, std::cref(readonly_data));
t.join();
// Store const references in containers
std::vector<std::reference_wrapper<const int>> const_refs;
const_refs.push_back(std::cref(readonly_data));

Real World Example

Here’s how I use reference wrappers in a simple event aggregator:

template<typename EventType>
class EventAggregator {
public:
using Handler = std::function<void(const EventType&)>;
private:
std::vector<std::reference_wrapper<Handler>> handlers_;
public:
void subscribe(Handler& handler) {
handlers_.push_back(std::ref(handler));
}
void publish(const EventType& event) {
for (auto& handler_ref : handlers_) {
handler_ref.get()(event); // Call the handler through reference
}
}
void unsubscribe(Handler& handler) {
handlers_.erase(
std::remove_if(handlers_.begin(), handlers_.end(),
[&handler](const std::reference_wrapper<Handler>& ref) {
return &ref.get() == &handler; // Compare addresses
}),
handlers_.end());
}
};
// Usage
EventAggregator<std::string> message_bus;
auto error_handler = [](const std::string& msg) {
std::cerr << "ERROR: " << msg << std::endl;
};
auto info_handler = [](const std::string& msg) {
std::cout << "INFO: " << msg << std::endl;
};
message_bus.subscribe(error_handler); // Store reference to existing handler
message_bus.subscribe(info_handler);
message_bus.publish("System started"); // Both handlers called
message_bus.unsubscribe(error_handler); // Remove specific handler
message_bus.publish("Running normally"); // Only info handler called

Working with Reference Wrappers

Reference wrappers have a few key methods:

int x = 42;
auto ref = std::ref(x);
// Get the underlying reference
int& actual_ref = ref.get();
// Implicit conversion to reference (in most contexts)
int value = ref; // Same as int value = ref.get();
// Assignment modifies the referenced object
ref = 100; // x is now 100
// Check the type
static_assert(std::is_same_v<decltype(ref), std::reference_wrapper<int>>);

The Pattern I Follow

I use std::ref when:

  • Passing references to functions that normally copy (like std::thread)
  • Storing references in containers
  • Working with algorithms that need to modify external state
  • Avoiding copies of expensive-to-copy objects
// Good candidates for std::ref
std::thread t(function_taking_reference, std::ref(existing_object));
std::vector<std::reference_wrapper<Observer>> observers;
std::bind(callback, std::ref(context), std::placeholders::_1);
// Usually don't need std::ref for these
auto lambda = [&obj](){ obj.method(); }; // Capture by reference works fine
function_taking_reference(obj); // Direct call doesn't need wrapper

std::ref is a specialized tool for specific situations. Most of the time, regular references and lambda captures handle what you need, but when you need to store references or pass them to copy-based APIs, reference wrappers are the solution.