Recently, during a technical meeting, a single line of code blew my mind. The talk was about a software library and how to use the C++ API. On one slide there was a statement of the sort

some_function(some_object) = some_value;

To experienced C++ developers, this might be nothing special. Even though I have completed larger projects in C++, I couldn’t remember having seen something like this before, not to mention having even used something like this.

As it turns out, such an assignment can be realized in (at least) two different ways.

  • Returning an lvalue
  • Returning a proxy object with an overloaded assignment operator

So obviously the key lies in the method some_function. Let’s have a look at two examples illustrating the two points from above.

Returning an lvalue

Assume we want to write a method which gives access to the first element of a vector. Passing the array as a reference to the method and simply returning the first entry doesn’t work. If you try

int first(vector<int>& array) {
  return array[0];
}

you will get a error: lvalue required as left operand of assignment message. Ok, so what’s an lvalue. To put it in simple terms, an lvalue is something that can appear on the left side of an assignment. When we return the first element of the array as an integer, C++ stores the integer as a temporary value—to be used in subsequent expressions. We can not assign to such a temporary value, because it doesn’t have a proper in place in memory. Usually, this value will only reside in CPU registers. The returned integer is an rvalue.1

We can modify the method such that it returns an lvalue, by changing its return type to int&. A reference (instead of a temporary result) is an lvalue. We can assign to a reference without any problem.

The full example using a templated function then reads.

// compile with: g++ -o lvalue -std=c++11 lvalue.cxx
#include <stdio.h>
#include <vector>

using std::vector;

// The 'magic' lies in the return type. Returning a reference to the first
// element, makes it possible to 'assign to the return value' of the function.
template <class T>
T& first(vector<T>& array) {
  return array[0];
}

// Use the accessor function in an example
int main() {
  vector<int> my_array = {0};  // Create initial array with single entry

  first(my_array) = 42;  // Return lvalue and assign to it

  // Print the content of the array
  printf("The first entry of the array is: %d\n", my_array[0]);

  return 0;
}

In fact, this shouldn’t be a surprise to me. I have used something returning lvalues all the time without even noticing. Writing my_array[0] = 42; uses the overloaded bracket operator which–if you check the implementation of vector in the standard template library–returns a reference to the element.

Returning proxy object

An alternative way to achieve this is by returning an object whose operator= method is overloaded. The implementation requires a bit more boilerplate code.

First, let’s create a templated class. The objects of the class should get a reference to an array during instantiation. A pointer to that object is stored. If you assign to the object, the overloaded assignment operator is called. Since we have more power using this alternative implementation, we will provide something more complex than a simple accessor for the first entry of the array. Instead, we will append the assigned value to the array. The class implementation looks as follows.

// compile with: g++ -o fancy_append -std=c++11 fancy_append.cxx
#include <stdio.h>
#include <vector>

using std::vector;

template <class T>
class Proxy {
 // Proxy class which appends a value to the given array when used in an
 // assignment.

 public:
  vector<T>* m_array;

  // Constructor of the class, stores pointer to array
  Proxy(vector<T>& array) {
    m_array = &array;
  }

  // Push the new value to the internally stored array
  void operator=(const T& value) {
    m_array->push_back(value);
  }
};

What’s left to do is to write a method that returns the proxy object. Since an assignment to the method will append to the array, we call it fancy_append.

// User-interface method, takes an array as argument. Values 'assigned to it'
// are appended to the array.
template <class T>
Proxy<T> fancy_append(vector<T>& array) {
  return Proxy<T>(array);
}

int main() {
  // Fill initial array
  vector<int> primes = {2, 3, 5};

  // Test fancy append
  fancy_append(primes) = 7;
  fancy_append(primes) = 11;

  // Print all values in the array
  for (auto const& i : primes) {
    printf("Value: %3d\n", i);
  }

  return 0;
}

When you run the above code, you see that the array contains all primes up to 11.

Summary

Seeing a line of C++ code like some_function(some_object) = some_value;, shouldn’t have been such a surprise to me, since it was around me all the time. However, if you have spent a lot of time in Python-land, where such statements are not possible, this kind of code looks quite unusual at first sight. I think this tells us a lot, how one gets used (not to say blind) to a programming language over time.


  1. Compile something like int a = first(my_array) + 9; and have a look at the output with objdump -d EXEC_FILE. With my setup, this contains the assembler instruction add $0x9,%eax after the call to first(). The rvalue was stored in the register eax