C++ References
Tags: cpp
Published: 2023-06-26 17:29:02

---
title: C++ References
published: true
description: Everything about C++ references, move constructors, and move assignment operators.
tags: #cpp
# cover_image: https://direct_url_to_image.jpg
# Use a ratio of 100:42 for best results.
# published_at: 2023-06-23 13:14 +0000
---

## Introduction

In addition to pointers inherited from C, C++ has _references_ that serve a similar purpose:

```cpp
int i;
int *p = &i;  // p points to i
*p = 42;      // i = 42
int &r = i;   // r refers to i
++r;          // ++i
```

Like a pointer, a _reference_ is a type of variable that refers (is bound) to some other object.  However, unlike a pointer, a reference:

+ **_Must_** be initialized (bound) to an object when defined.
+ Can **_not_** be rebound to a different object.  (There’s no syntax to do it.)
+ Can never be null.

But if C++ already has pointers, why were references added to C++?

## Cheaply-Copied Types

Before we get to why references were added to C++, we need to define the term _cheaply-copied type_:

> A C++ type T is considered “cheaply copied” when the cost of making a copy is less than or on par with the cost of indirecting through a pointer or reference.

What constitutes “cheaply copied” varies by machine architecture, but generally includes any type T where `sizeof(T)` &le; 16, so:

+ Any built-in type (e.g., `unsigned`).
+ Any `enum`.
+ Any pointer.
+ Any small `struct`, `union`, or `class`.
+ Any `typedef` thereof.

As an exception, a class that manage resources (for example, an object T that contains a pointer to another object U where copying T includes copying U) is _not_ cheaply copied even if `sizeof(T)` &le; 16.

## Motivation for References

According to [Bjarne](https://www.stroustrup.com/bs_faq2.html#pointers-and-references), references were added to C++ primarily to support operator overloading.  The goal is to be able to pass arguments to overloaded operators efficiently even when the argument types are _not_ cheaply-copied.  The only way to do that without references is with pointers, for example:

```cpp
T operator+( T const *lhs, T const *rhs );

void f() {
   T x, y;
   // ...
   T z = &x + &y;  // ugly
}
```

That’s ugly.  With references, it gets better:

```cpp
T operator+( T const &lhs, T const &rhs );

void f() {
   T x, y;
   // ...
   T z = x + y;    // better
}
```

References are also useful when passing non-cheaply-copied types to or returning such types from functions in general:

```cpp
std::string const* get_string();
void print_string( std::string const *s );

void f() {
    std::string s{ *get_string() };  // ugly
    // ...
    print_string( &s );              // also ugly
}
```

## Not for Output Parameters

References can also be used instead of pointers for “output parameters” of functions to receive “return values”:

```cpp
bool parse_T( std::string const &s, T &t_out );

void f( std::string const &s ) {
    T t;
    if ( !parse_T( s, t ) )   // Is 't' modified?
        // complain
}
```

The problem with this you can’t tell by looking at uses of `parse_T()` whether `t` is modified or not because there’s no visible difference between passing `t` by value versus by non-`const` reference.  To find out, you have to find the declaration of `parse_T()` to see whether the reference is `const` or not.

> Even though there’s no visible difference between passing `t` by value versus by `const&` either, you don’t care because `t` _can’t_ be modified.  From your point of you, it would make no semantic difference whether `t` were passed by `const&` or by value.  The use of `const&` is an optimization to pass a non-cheaply-copied type efficiently.

However, if you use a pointer instead:

```cpp
bool parse_T( std::string const &s, T *t_out );

void f( std::string const &s ) {
    T t;
    if ( !parse_T( s, &t ) )  // '&' hints 't' is modified.
        // complain
}
```

then the presence of `&` is a visual hint that `t` is likely modified.

Consequently, I personally recommend that non-`const` references _not_ be used for output parameters and that pointers be used instead.  To add weight to this recommendation, it happens that [Bjarne agrees](https://www.stroustrup.com/bs_faq2.html#call-by-reference):

> _“My personal style is to use a pointer when I want to modify an object because in some contexts that makes it easier to spot that a modification is possible.”_ —&nbsp;Bjarne&nbsp;Stroustrup

## Not for Data Members Either

References can be used as data members of classes:

```cpp
class C {
public:
    C( T &t ) : _t{ t } { }
    // ...
private:
    // ...
    T &_t;
};
```

One advantage of using references instead of pointers for data members is that you can’t forget to initialize references in every constructor. However, there are disadvantages:

+ The existence of at least one reference data member will suppress the implicitly defined assignment operators because references can not be reassigned (rebound).

+ The existence of at least one reference data member means you can not copy (rebind) references in your own assignment operators either.

+ Have a problem similar to the use of non-`const` references for output parameters in that you can’t tell by looking at uses that they’re not part of the class itself:
    ```cpp
    _t.f();   // Is '_t' part of C?
    ```
    However, if you use a pointer instead:
    ```cpp
    _t->f();  // Says '_t' points elsewhere.
    ```

## Lvalues & Rvalues

Before continuing, we need to digress a bit to define the terms _lvalue_ and _rvalue_ that are types of _value category_:

+ An _lvalue_ is a value, **_has a name_**, can (but _not_ must) appear on the left-hand-side (LHS) of `=` (the assignment operator), and can have its address taken via `&` (the address-of operator).

+ An _rvalue_ is a _temporary_ value, **_has no name_**, can _only_ appear on the right-hand-side (RHS) of `=`, and can _not_ have its address taken via `&`.

+ The key thing that distinguishes and lvalue from an rvalue is: if something has a name, it’s **_not_** an rvalue.

> These value categories were actually updated in C++11 by being [renamed and expanded](https://en.cppreference.com/w/cpp/language/value_category), but they’re quite a bit more complicated now — unnecessarily so for this article. The old terms of _lvalue_ and _rvalue_ are sufficient here.

Some examples:

```cpp
int a, b, *p;

a = b;         // "a" and "b" are lvalues (both have names)
a = a + b;     // "a" = lvalue; "a + b" = rvalue
a + b = 42;    // error: rvalue on left-hand-side

p = &a;        // address of lvalue
p = &(a + b);  // error: address of rvalue
```

## Motivation for Rvalue References

Given:

```cpp
class container {
public:
    // ...
    void push_back( std::string const &s );
};

int main() {
    container c;
    c.push_back( "hello" );
}
```

This code will compile and work as expected despite _not_ having:
```cpp
    void push_back( char const *s );
```
because `std::string` has a constructor:
```cpp
    string( char const *s );
```
that the compiler will use to construct a temporary `string` from `"hello"` that can then be passed to the `push_back()` that exists as if:
```cpp
int main() {
    container c;
    {
        std::string __tmp{ "hello" };
        c.push_back( __tmp );
    }
}
```

More explicitly:
1. For `__tmp`, `string::string( char const* )` is called that dynamically allocates a buffer and copies the string literal `"hello"` into it.
2. `push_back( std::string const& )` adds that `string` to its container via `string::string(std::string const&)` that dynamically allocates another buffer and copies the string into it.
3. `string::~string()` destroys `__tmp` by deallocating the first buffer.

That’s inefficient.  If `__tmp` is going to be destroyed anyway, why not _move_ its buffer by moving _only_ the string’s internal pointer rather than copying the whole buffer?  This is precisely why _rvalue references_ were added to C++.

## Rvalue References

C++11 added _rvalue references_ that refer to temporary objects for the purpose of “stealing” their resources for efficiency.  (Plain references have since been known as _lvalue references_.)  Rvalue references are declared with `&&`.

The previous example can be augmented:

```cpp
class container {
public:
    // ...
    void push_back( std::string const &s );
    void push_back( std::string &&s );  // rvalue reference
};

int main() {
    container c;
    std::string s{ "hello" };
    c.push_back( s );        // push_back( std::string const& );
    c.push_back( "world" );  // push_back( std::string&& );
}
```

As before, in the case of `"world"`, the compiler will create a temporary `string` object.  But now, the new overload of `push_back( std::string &&s )` allows the compiler to call _that_ function preferentially with the temporary object bound to the rvalue reference.  The implementation of that `push_back()` will “steal” (move) the string rather than copy it.

However, this requires cooperation from `std::string` that must provide its own handling of rvalue references.  A simplified partial declaration is:

```cpp
class string {
public:
    string();
    string( char const *s );
    string( string const &s );             // copy constructor
    string( string &&s );                  // move constructor

    string& operator=( char const *s );
    string& operator=( string const &s );  // copy assignment
    string& operator=( string &&s );       // move assignment

    // ...
private:
    char *_buf;
    size_type _cap, _len;                  // capacity & length
};
```

Specifically, `string` now also has a _move constructor_ and _move assignment_ operator that constructs or assigns from an rvalue reference bound to a temporary `string`.  The move constructor is implemented something like:

```cpp
string::string( std::string &&from ) :
    _buf{ std::exchange( from._buf, nullptr ) },
    _cap{ std::exchange( from._cap, 0 ) },
    _len{ std::exchange( from._len, 0 ) }
{
}
```

That is it copies `_buf` (just the pointer, not the string), `_cap`, and `_len` and sets `from._buf` to `nullptr` and both `from._cap` and `from._len` to 0.  This effectively “steals” (moves) `from`’s string leaving it empty — which is fine because, remember, it’s a temporary object that will soon be destroyed anyway.  (The move assignment operator is implemented similarly.)

The addition of move constructors and move assignment operators to classes improves their efficiency with temporary objects, but only when at least one of their data members is a non-cheaply-copied type.  A class like:

```cpp
class point2d {
    int _x, _y;
public:
    point2d() : point2d{ 0, 0 } { }
    point2d( int x, int y ) : _x{ x }, _y{ y } { }
    // ...
};
```

wouldn’t benefit from adding a move constructor or move assignment operator because copying `int`s is already cheap and can’t be made more efficient.

## Default Move Constructors & Assignment Operators

Just as the compiler will automatically synthesize copy constructors and copy assignment operators for your class when possible, it will also synthesize move constructors and move assignment operators for your class when possible.

So when should you implement them yourself?  The general rule is: if it’s necessary to implement the copy constructor or copy assignment yourself, you should at least think about whether it would be more efficient to implement their move counterparts yourself.  This is part of [the rule of 3/5/0](https://en.cppreference.com/w/cpp/language/rule_of_three).

## `std::move(T)`

Consider the following:

```cpp
void f( container *pc ) {
   std::string s{ "hello" };
   pc->push_back( s );        // push_back( std::string const& )
}
```

This will call `push_back( std::string const& )` and _copy_ the string even though we can see that `s` is about to be destroyed by returning from the function.  Why doesn’t the compiler call `push_back( std::string&& )` instead?  Because `s` has a name (remember: if something has a name, it’s **_not_** an rvalue) and the compiler isn’t smart enough to see that `s` will soon be destroyed anyway.

To get the compiler to call `push_back( std::string&& )`, we have to convert the lvalue reference that is `s` to an rvalue reference. This is precisely what `std::move()` does:

```cpp
void f( container *pc ) {
   std::string s{ "hello" };
   pc->push_back( std::move( s ) ); // push_back( std::string&& )
}
```

Using `std::move()` tells the compiler that you want to call a function that accepts an rvalue reference, if one exists.  (If no such function exists, the use of `std::move()` will have no effect and the compiler will call a function that accepts an lvalue reference instead.)

A typical implementation of `std::move()` is:

```cpp
template<typename T>
inline std::remove_reference_t<T>&& move( T &&arg ) {
    return static_cast<std::remove_reference_t<T>&&>( arg );
}
```

Hence, it’s really just a shorthand for a verbose `static_cast` to an rvalue reference. Being just a cast, `std::move()`:

1. Incurs _zero_ run-time performance penalty — it’s strictly compile-time.
2. Is a misnomer since it doesn’t actually move anything.
3. Does _not_ guarantee that the argument will actually be moved.

The cast to an rvalue reference allows the compiler to call functions that have an rvalue reference parameter making the argument _eligible_ to be moved, but the called functions are the ones that actually do the moving.

## Rvalue References in Class Hierarchies

Suppose you have a base and a derived class and each provides a move constructor in addition to its copy constructor:

```cpp
class B {
public:
    B( B const& );
    B( B&& );
    // ...
};

class D : public B {
public:
    D( D const& );
    D( D&& );
    // ...
};
```

You might think the implementation of `D`’s constructors would be:

```cpp
D::D( D const &from ) :
    B{ from }
{
}

D::D( D &&from ) :
    B{ from }               // WRONG: calls B( B const& )
{
}
```

However, the implementation of `D::D( D&& )` is **_wrong_** because even though the type of `from` is an rvalue reference causing it to receive rvalues, once received and bound to a name, it “decays” into a lvalue.  (Remember: if something has a name, it’s **_not_** an rvalue.) To convert it back to an rvalue, you need to use `std::move()` again:

```cpp
D::D( D &&from ) :
    B{ std::move( from ) }  // correct: calls B( B&& )
{
}
```

## Motivation for Forwarding References

Suppose you want to implement a wrapper function to create objects of any type T and pass a single argument.  You might write something like:

```cpp
template<typename T, typename Arg>
T create_T( Arg &arg ) {
    // ...
    return T{ arg };
}
```

However, if you attempt to use it with a literal like:

```cpp
T x = create_T( 42 );  // error
```

you’d get an error because you can’t initialize a non-`const&` with a literal. To fix this, you can add an overload:

```cpp
template<typename T, typename Arg>
T create_T( Arg const &arg ) {
    // ...
    return T{ arg };
}

T x = create_T( 42 );  // ok (now)
```

While that works, suppose you now want to allow rvalue references as well.  To do that, you’d have to add yet a third overload:

```cpp
template<typename T, typename Arg>
T create_T( Arg &&arg ) {
    // ...
    return T{ std::move( arg ) };
}

T x = create_T( std::move( arg ) );
```

That’s rather tedious.  If you think that’s bad, if `create_T()` were to take two parameters, you’d need _nine_ overloads to have every combination of non-`const&`, `const&`, and `&&`.  In general, you need 3<sup>N</sup> overloads for N parameters.  Clearly, this is unworkable.  This is precisely why _forwarding references_ (aka, _universal references_) were added to C++.

> Formally, such references are known as “forwarding references”; however, [Scott Meyers](https://en.wikipedia.org/wiki/Scott_Meyers) coined the term “[universal references](https://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers)” that describes what they _do_ rather than what they’re _for_.

## Forwarding References

The way to define `create_T()` _once_ and have it work with all non-`const&`, `const&`, and `&&` combinations is:

```cpp
template<typename T, typename Arg>
T create_T( Arg &&arg ) {
    // ...
    return T{ std::forward<Arg>( arg ) };
}
```

While `std::move()` was replaced by `std::forward()`, the declaration of `create_T()` itself hasn’t changed.  That’s because `X&&` for type X in a “type deduction context” (template) is a _universal reference_ meaning it can bind to _any_ kind of reference.  That is the same syntax of `&&` can either be an rvalue reference **_or_** a universal reference depending on the declaration.  This can be summarized as:

Case | Declaration                       | Rvalue Reference?
:--: | --------------------------------- | :---------------:
  1  | `T &&x = f();`                    | ✅
  2  | `void f( std::vector<T> &&arg );` | ✅
  3  | `void f( T &&arg );`              | ❌
  4  | `void f( T const &&arg );`        | ✅

Cases 1, 2, and 4 declare rvalue references (as always). However, case 3 — that **_must exactly_** match the pattern `T&&` — declares a forwarding reference. Notice that even adding `const` as in case 4 does _not_ match the pattern.

> Why did the C++ Committee make `&&` confusingly mean two different things depending on context? They didn’t. The fact that the syntax of `T&&` came to function as a forwarding reference is a happy (?) accident of the separate [reference collapsing rules](https://en.cppreference.com/w/cpp/language/reference#Reference_collapsing) in C++.  Adding a new syntax to declare forwarding references would have been a much bigger change to C++.

While that explains the `Arg&&` in the declaration of `create_T()`, what does `std::forward()` do?  While `std::move()` _always_ converts and lvalue reference to an rvalue reference, `std::forward()` _forwards_ a reference as-is.  In the line:

```cpp
    return T{ std::forward<Arg>( arg ) };
```

`arg` has “decayed” into a lvalue reference because it has a name.  (Remember: if something has a name, it’s **_not_** an rvalue.)  If `Arg` is an rvalue type, then `std::forward()` behaves like `std::move()` and converts `arg` (now an lvalue because it has a name) back into an rvalue. However, if `Arg` is an lvalue type, then `std::forward()` leaves it alone.  This is known as _perfect forwarding_.

## Returning by Rvalue Reference

You might wonder whether it helps to return by rvalue reference like:

```cpp
T&& f() {                 // return by rvalue reference
    T v;
    // ...
    return std::move( v );
}
```

The intent is to avoid copying `v` (that will be destroyed shortly anyway) and move it to its final destination instead.  Unfortunately, this is equivalent to:

```cpp
T& f() {                  // return by lvalue reference
    T v;
    // ...
    return v;             // WRONG: reference to temporary
}
```

This should now be obviously wrong because it will create a dangling reference to a destroyed object!  To avoid copying, you simply return by value:

```cpp
T f() {                   // return by value
    T v;
    // ...
    return v;             // correct: uses NRVO
}
```

In this one specific case, the compiler is smart enough to realize that `v` should be treated as a temporary (_despite_ having a name) and to [elide the copy](https://en.cppreference.com/w/cpp/language/copy_elision) via the _named return value optimization_ (NRVO).

You might also wonder whether returning by value _and_ using `std::move()` helps:

```cpp
T f() {                   // return by value
    T v;
    // ...
    return std::move(v);  // WRONG: prevents NRVO
}
```

The short answer is no.  The reason is that using `std::move()` results in an rvalue expression (that has no name) and you can’t do NRVO with no name!

> There are actually a _very_ few legitimate cases for returning by rvalue reference.  For example, both `std::move()` and `std::forward()` do. Generally, however, it’s very likely wrong.

## Miscellaneous Reference Declarations

You might also wonder whether any of the following rvalue reference declarations are useful:

```cpp
void f( T const &&p ) {  // rvalue reference to const parameter
    T &&x = g();         // local rvalue reference
}
```

The short answer is no:

+ A `const&&` parameter defeats the entire purpose of an rvalue reference that is to move objects. The reference _must_ be non-`const` since the move’d-from object is modified.

+ An rvalue reference local variable is also useless.  While it can bind to an rvalue expression, it immediately “decays” into an lvalue because it has a name. (Remember: if something has a name, it’s **_not_** an rvalue.)

## Conclusion

C++ has always had lvalue references:

+ As [syntactic sugar](https://en.wikipedia.org/wiki/Syntactic_sugar) for overloaded operator arguments.
+ To pass and return non-cheaply-copied types to and from functions.

C++11 added rvalue references:

+ For moving objects (to avoid copying).

C++11 also added forwarding references:

+ Forwards function parameter references as-is for perfect forwarding.