Skip to Content
Course content

247: std::reference_wrapper

Click on the "Edit" button in the top corner of the screen to edit your slide content.

You've probably noticed by now that C++ references are great for passing arguments to functions without copying large objects. But there's a frustrating limitation: references aren't "objects" in the eyes of the language. They can't be rebound once initialized, and more importantly, they can't be stored in a container. You can't have a std::vector<T> where T is a reference.

I ran into this recently while building a simple targeting system for a small game project. I had a bunch of Enemy objects owned by a World class, and I wanted a TargetList that kept track of which enemies were currently being targeted by the player. I didn't want to use std::shared_ptr because the World should remain the sole owner of the memory—I just wanted a way to point to them.

The trap of the reference vector

My first instinct was to keep it simple. I tried to create a vector of references to Enemy objects. It looked something like this:

struct Enemy {
    std::string name;
    int health;
};

// I wanted to do this:
std::vector<Enemy> targets; 

Of course, the compiler immediately started screaming at me. You can't have a std::vector of references because the elements of a vector must be "erasable," which implies they need to be assignable. Since a C++ reference cannot be rebound to a different object after it's created, it doesn't meet the requirements for a container element. It's a common point of friction.

Wrapping the reference

This is where std::reference_wrapper comes in. It's essentially a small class template that holds a pointer internally but behaves like a reference. Because it's a class (an actual object), it can be copied and assigned, making it perfectly legal to put inside a std::vector.

I swapped out my broken vector for this:

#include <vector>
#include <functional> // Required for std::reference_wrapper
#include <iostream>

struct Enemy {
    std::string name;
    int health;
};

int main() {
    Enemy goblin{"Goblin", 30};
    Enemy orc{"Orc", 60};

    // Instead of Enemy&, we use reference_wrapper
    std::vector<std::reference_wrapper<Enemy>> targets;

    // We use std::ref() to create the wrapper easily
    targets.push_back(std::ref(goblin));
    targets.push_back(std::ref(orc));
}

Now the code compiles. std::ref is a helper function that creates a std::reference_wrapper for you. It's much cleaner than typing out the full template name every time you push something into the list.

Interacting with the wrapped objects

One thing that tripped me up for a second was how to actually use the objects once they're in the vector. I tried to access a member directly, like targets[0].health, and the compiler told me std::reference_wrapper has no member named health. I forgot that the wrapper is a shell; it's not the object itself.

To get to the actual Enemy, you have two choices. You can call .get(), or you can rely on the fact that std::reference_wrapper has an implicit conversion operator to the underlying type.

for (Enemy& e : targets) {
    // This works because of implicit conversion
    std::cout < "Targeting: " < e.name < "\n";
}

// Or, if you need the reference explicitly:
Enemy& firstTarget = targets[0].get();
firstTarget.health -= 10;

I personally prefer the implicit conversion in range-based for loops—it makes the wrapper feel almost invisible. Just keep in mind that std::reference_wrapper doesn't track the lifetime of the object. If the World destroys an Enemy but that enemy is still in your targets list, you've got a dangling reference. It's the same risk as a raw pointer, just with a prettier interface.




📋 Practical Task

Implement a "Selected Units" Manager

You are building a strategy game. You have a Unit class with a unitID and a position. You need to implement a SelectionManager class that can track multiple units without taking ownership of them (the units are owned by a Map class elsewhere).

Requirements:

  • Create a Unit struct with int unitID and std::string type.
  • Create a SelectionManager class that contains a std::vector of std::reference_wrapper<Unit>.
  • Implement a method void selectUnit(Unit& u) that adds a unit to the selection.
  • Implement a method void printSelection() that iterates through the selected units and prints their ID and type.
  • In your main function, create three different Unit objects on the stack, select two of them, and call printSelection() to verify it works.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.