std::ref and Reference Wrappers
/ 5 min read
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 wrapperstd::cout << x << std::endl; // Prints 100The 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 insteadstd::vector<std::reference_wrapper<int>> refs = {std::ref(a), std::ref(b), std::ref(c)};
// Modify through the wrappersfor (auto& ref : refs) { ref.get() *= 2; // .get() returns the actual reference}
// a=2, b=4, c=6When I Actually Use std::ref
Most of the time, I use std::ref for:
- 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 referencestd::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});- 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 referencestd::thread t(worker_function, std::ref(data));t.join();
std::cout << data << std::endl; // Should be 1000- 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; }};
// UsageEventSystem events;LogObserver logger;events.add_observer(logger); // Store reference to existing objectevents.notify_all("Something happened");- 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 variablesauto 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::crefstd::thread t(print_value, std::cref(readonly_data));t.join();
// Store const references in containersstd::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()); }};
// UsageEventAggregator<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 handlermessage_bus.subscribe(info_handler);
message_bus.publish("System started"); // Both handlers calledmessage_bus.unsubscribe(error_handler); // Remove specific handlermessage_bus.publish("Running normally"); // Only info handler calledWorking with Reference Wrappers
Reference wrappers have a few key methods:
int x = 42;auto ref = std::ref(x);
// Get the underlying referenceint& actual_ref = ref.get();
// Implicit conversion to reference (in most contexts)int value = ref; // Same as int value = ref.get();
// Assignment modifies the referenced objectref = 100; // x is now 100
// Check the typestatic_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::refstd::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 theseauto lambda = [&obj](){ obj.method(); }; // Capture by reference works finefunction_taking_reference(obj); // Direct call doesn't need wrapperstd::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.