Ссылки, lvalue и rvalue
Ссылка (reference) бывает не только параметром (parameter) функции. Её можно объявить и как обычную переменную — тогда она становится вторым именем другой переменной:
#include <iostream>
int main() {
int score = 10;
int& ref = score;
ref = ref + 5;
std::cout << score << '\n'; // 15
}ref — не новая переменная и не копия. ref и score обозначают одно место в памяти, поэтому изменение через одно имя видно через другое. Ссылка, инициализированная другой ссылкой, привязывается к той же переменной: после int& alias = ref; имя alias тоже обозначает score.
Одна переменная навсегда
Переменную для ссылки выбирают один раз — при объявлении (declaration). Поэтому ссылка без инициализатора не компилируется: int& ref; — ошибка. Перепривязать ссылку к другой переменной потом тоже нельзя. Присваивание через ссылку меняет значение переменной, к которой она привязана:
#include <iostream>
int main() {
int first = 10;
int second = 20;
int& ref = first;
ref = second;
std::cout << first << ' ' << second << '\n'; // 20 20
}Строка ref = second; записывает в first значение second, то есть 20. После неё ref по-прежнему обозначает first: если потом изменить second, ни first, ни ref не изменятся.
К чему привязывается ссылка
Выражение, которое обозначает переменную, называют lvalue: например, score или ref. Литерал 5 или результат score + 1 — это rvalue: значение без имени, которое не хранится ни в одной переменной. Ссылка int& привязывается только к lvalue.
Константная ссылка const int& принимает и rvalue. Для такого значения компилятор (compiler) создаёт временный объект (temporary object) — безымянную переменную, в которой оно хранится, — и привязывает ссылку к нему. Такой объект живёт, пока существует ссылка:
#include <iostream>
int main() {
int score = 10;
const int& next = score + 1;
score = 50;
std::cout << next << '\n'; // 11
}next обозначает временный объект со значением 11, а не score, поэтому изменение score его не задевает. По той же причине параметр const int& принимает литералы и выражения.
Частая ошибка
Обычную ссылку привязывают к результату выражения:
int main() {
int a = 2;
int b = 3;
int& sum = a + b; // ✗
return sum;
}error: non-const lvalue reference to type 'int' cannot bind to a temporary of type 'int'a + b — rvalue: сумма не хранится ни в одной переменной, поэтому int& к ней не привязывается. Если менять sum не нужно, объявите её как const int&. Но чаще здесь нужна обычная переменная: int sum = a + b;.