Skip to content

Instantly share code, notes, and snippets.

@cswiercz
Last active May 11, 2020 21:13
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cswiercz/b2101c04643ddbabde91cff93de689b6 to your computer and use it in GitHub Desktop.
Save cswiercz/b2101c04643ddbabde91cff93de689b6 to your computer and use it in GitHub Desktop.
C++11 Reference Wrappers

My Problems (There Are Many)

The C++ standard library containers are nice. Like, really nice. As the kind of person who worries about performance to the point that they were shy of anything other than a raw data array I've come to appreciate the usability (and speed!) of ye olde std::vector. Does that many me sound like a wannabe oldie?

Recently I came across a situation where I had, say, a vector [a, b, c, d] and wanted to create the vectors [a, b, c], [a, b, d], [a, c, d], and [b, c, d]. I didn't want to actually allocate O(N(N-1)) additional memory but learned that std::vector didn't support holding references. That's when I came across std::reference_wrapper.

Reference Wrappers

From the Cppreference page:

std::reference_wrapper is a class template that wraps a reference in a copyable, assignable object. It is frequently used as a mechanism to store references inside standard containers (like std::vector) which cannot normally hold references.

Example

In the example below we will create a vector of ints, refer to the elements using a vector of reference wrappers, and demonstrate that we can modify either the original vector or the referencing vector.

First, some preamble stuff and intialization of a data array.

#include <iostream>
#include <random>
#include <vector>
 
int main()
{
  size_t N = 5;
  std::vector<int> l(N);
  std::iota(l.begin(), l.end(), -N/2+1);
 
  // ...
}

Next, we will create a vector of references to the elements of the original vector

  // ...
  
  std::vector<std::reference_wrapper<int>> v; 
  for (int &elt : l) v.emplace_back(elt);
  
  // ...

In the loop a reference wrapper is created for each element in l and that reference wrapper is appended to the vector v. Let's verify that the values and addresses of these vectors are identical.

  // ...
  
  std::cout << "Elements:\n";
  std::cout << "l:\t"; for (int elt : l) std::cout << elt << ' '; std::cout << '\n';
  std::cout << "v:\t";for (int elt : v) std::cout << elt << ' '; std::cout << '\n';

  std::cout << "Addresses:\n";
  std::cout << "l:\t"; for (int &elt : l) std::cout << &elt << ' '; std::cout << '\n';
  std::cout << "v:\t"; for (int &elt : v) std::cout << &elt << ' '; std::cout << '\n';
  
  // ...

Compiling and running this code up to this point gives the following output:

[csw.local:tmp] % clang++ --std=c++11 ./ref-wrapper.cpp && ./a.out
Elements:
l:	-2 -1 0 1 2 
v:	-2 -1 0 1 2 
Addresses:
l:	0x7fe9834026e0 0x7fe9834026e4 0x7fe9834026e8 0x7fe9834026ec 0x7fe9834026f0 
v:	0x7fe9834026e0 0x7fe9834026e4 0x7fe9834026e8 0x7fe9834026ec 0x7fe9834026f0 

Now, let's edit the values in the original vector.

  // ...
  
  std::cout << "\nDoubling elements of the original vector...\n\n";
  for (int &elt : l)
    elt *= 2;

  std::cout << "Elements:\n";
  std::cout << "l:\t"; for (int elt : l) std::cout << elt << ' '; std::cout << '\n';
  std::cout << "v:\t"; for (int elt : v) std::cout << elt << ' '; std::cout << '\n';

  std::cout << "Addresses:\n";
  std::cout << "l:\t"; for (int &elt : l) std::cout << &elt << ' '; std::cout << '\n';
  std::cout << "v:\t"; for (int &elt : v) std::cout << &elt << ' '; std::cout << '\n';
  
  // ...

The corresponding output shows that (a) the addresses are the same and (b) the references are working.

Doubling elements of the original vector...

Elements:
l:	-4 -2 0 2 4 
v:	-4 -2 0 2 4 
Addresses:
l:	0x7fe9834026e0 0x7fe9834026e4 0x7fe9834026e8 0x7fe9834026ec 0x7fe9834026f0 
v:	0x7fe9834026e0 0x7fe9834026e4 0x7fe9834026e8 0x7fe9834026ec 0x7fe9834026f0 

We can, of course, modify the elements of the reference vector v. Since each element points to a corresponding element in l this is the same as the code above.

  // ...
  
  std::cout << "\nDoubling elements of the referencing vector...\n\n";
  for (int &elt : v)
    elt *= 2;

  std::cout << "Elements:\n";
  std::cout << "l:\t"; for (int elt : l) std::cout << elt << ' '; std::cout << '\n';
  std::cout << "v:\t"; for (int elt : v) std::cout << elt << ' '; std::cout << '\n';

  std::cout << "Addresses:\n";
  std::cout << "l:\t"; for (int &elt : l) std::cout << &elt << ' '; std::cout << '\n';
  std::cout << "v:\t"; for (int &elt : v) std::cout << &elt << ' '; std::cout << '\n';
  
} // end of int main() 

And the output to this block of code.

Doubling elements of the referencing vector...

Elements:
l:	-8 -4 0 4 8 
v:	-8 -4 0 4 8 
Addresses:
l:	0x7fe9834026e0 0x7fe9834026e4 0x7fe9834026e8 0x7fe9834026ec 0x7fe9834026f0 
v:	0x7fe9834026e0 0x7fe9834026e4 0x7fe9834026e8 0x7fe9834026ec 0x7fe9834026f0 
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment