References in C++

Jinku Hu Oct 12, 2023
  1. Use the & attr(optional) declarator Notation to Declare an lvalue Reference in C++
  2. Use References to Implement Function Prototypes in C++
References in C++

This article demonstrates the multiple methods of how to use references in C++.

Use the & attr(optional) declarator Notation to Declare an lvalue Reference in C++

References define alternative names for objects. They are not objects themselves, and consequently, we can not have arrays of references or pointers to references. References only refer to already existing objects, and they are bound to these objects, implying that no modification is allowed after the declaration.

Additionally, references must be initialized, and when the operation is conducted using the reference names, the bound objects are modified. Notice that the latter feature makes them quite similar to the pointers. We can consider references as alternatives to pointers, except that the former can be used as regular variable names without a dereference notation to access the value of the corresponding object. If we want to imitate the behavior of references, we can declare a const pointer to the given type. The latter is characterized with the same features as references except for the notation of accessing the values.

In the following example code, the variable xr is declared as the reference to an x, representing an int object. Once xr is initialized during the declaration, it can not be reassigned to refer to a different object of any type. On the other hand, we can define different alias names such as xr to the same object it refers to. Namely, xrc is a const qualified reference which means that the corresponding object’s value can only be accessed and can not be modified using the alias name.

#include <iostream>

using std::cout;
using std::endl;

int main() {
  int x = 5;
  int& xr = x;
  const int& xrc = x;

  xr *= xr;
  //    xrc *= x; Error
  cout << "xr: " << xr << endl;
  cout << "xrc: " << xrc << endl;

  return EXIT_SUCCESS;
}

Output:

xr: 25
xrc: 25

We can declare multiple references to the same object, as demonstrated in the following code snippet. Still, one should be aware that modifications from any of the aliases will affect the same object.

#include <iostream>

using std::cout;
using std::endl;

int main() {
  int x = 5;
  int& xr = x;
  int& xrr = x;

  xr *= xr;
  xrr *= xrr;
  cout << "xr: " << xr << endl;
  cout << "xrr: " << xrr << endl;

  return EXIT_SUCCESS;
}

Output:

xr: 625
xrr: 625

Use References to Implement Function Prototypes in C++

References are primarily utilized to provide cleaner function interfaces, but also because the C++ language provides operator-overloading capabilities, and the notation should be the same for all operators. The latter would be complicated if there were no reference concepts in the language. Thus, using the operators that need to take arguments as pointers could have looked like ++&x.

Functions that need to take objects for accessing values can specify the corresponding arguments as const references and if the modification is also required without const. For example, the printVector function takes a const reference to std::vector to print the elements to the cout stream, but swapElements need to swap each element, so we need to have regular reference to the object.

#include <algorithm>
#include <iomanip>
#include <iostream>
#include <string>
#include <vector>

using std::cout;
using std::endl;
using std::setw;
using std::string;
using std::vector;

template <typename T>
void printVector(const vector<T> &vec) {
  for (auto &i : vec) {
    cout << i << "; ";
  }
  cout << endl;
}

template <typename T>
void swapElements(vector<T> &vec) {
  for (auto iter = vec.begin(); iter < vec.end() - 1; ++iter) {
    std::swap(*iter, *(iter + 1));
  }
}

int main() {
  vector<int> vec1 = {43, 5, 123, 94, 359, -23, 2, -1};

  printVector(vec1);
  swapElements(vec1);
  printVector(vec1);

  return EXIT_SUCCESS;
}

Output:

43; 5; 123; 94; 359; -23; 2; -1;
5; 123; 94; 359; -23; 2; -1; 43

Note that all previous examples of references, including the const ones, are called lvalue references. These references are used to refer to the objects that need to preserve valid values after we access or modify them. The C++ language also provides rvalue references, which are utilized to refer to the temporary objects.

A temporary object is an assumption for the programmer that the given object won’t be used again. The rvalue references are specified using the && attr(optional) declarator notation, and the objects are accessed like the lvalue references. Rvalue references are utilized to implement efficient operations, like move the constructor and move the assignment to avoid expensive copies of large objects.

Author: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

Founder of DelftStack.com. Jinku has worked in the robotics and automotive industries for over 8 years. He sharpened his coding skills when he needed to do the automatic testing, data collection from remote servers and report creation from the endurance test. He is from an electrical/electronics engineering background but has expanded his interest to embedded electronics, embedded programming and front-/back-end programming.

LinkedIn Facebook

Related Article - C++ Reference