Объявление ссылок
Объявляет именованную переменную как ссылку, то есть псевдоним уже существующего объекта или функции.
Синтаксис
Объявление переменной-ссылки — это любое простое объявление, у которого декларатор имеет вид
& attr (необязательно) декларатор |
(1) | |
&& attr (необязательно) декларатор |
(2) | (с C++11) |
S& D; объявляет D как ссылку на lvalue типа, определяемого decl-specifier-seq S. S&& D; объявляет D как ссылку на rvalue типа, определяемого decl-specifier-seq S. | декларатор | - | любой декларатор, кроме другого декларатора ссылки (ссылок на ссылки не существует) |
| attr | - | (с C++11) список атрибутов |
Ссылка должна быть инициализирована для ссылки на допустимый объект или функцию: см. инициализацию ссылок.
Тип «ссылка на (возможно, с квалификаторами cv) void» не может быть сформирован.
Типы ссылок не могут быть квалифицированы cv на верхнем уровне; для этого в объявлении нет синтаксиса, и если квалификация добавляется к имени typedef или decltype спецификатору,(с C++11) или параметру шаблона типа, она игнорируется.
Ссылки не являются объектами; они не обязательно занимают память, хотя компилятор может выделить память, если это необходимо для реализации желаемой семантики (например, нестатическое поле типа ссылка обычно увеличивает размер класса на количество байтов, необходимое для хранения адреса памяти).
Поскольку ссылки не являются объектами, нет массивов ссылок, указателей на ссылки и ссылок на ссылки:
int& a[3]; // error int&* p; // error int& &r; // error
Сворачивание ссылокРазрешается формировать ссылки на ссылки посредством манипуляций с типами в шаблонах или typedef, в этом случае применяются правила сворачивания ссылок: ссылка на rvalue на rvalue сворачивается в ссылку на rvalue, все остальные комбинации образуют ссылку на lvalue: typedef int& lref; typedef int&& rref; int n; lref& r1 = n; // type of r1 is int& lref&& r2 = n; // type of r2 is int& rref& r3 = n; // type of r3 is int& rref&& r4 = 1; // type of r4 is int&& (Это, наряду со специальными правилами для вывода аргументов шаблонов, когда |
(с C++11) |
Ссылки на lvalue
Ссылки на lvalue могут использоваться для алиасинга существующего объекта (возможно, с другими квалификаторами cv):
#include <iostream>
#include <string>
int main()
{
std::string s = "Ex";
std::string& r1 = s;
const std::string& r2 = s;
r1 += "ample"; // modifies s
// r2 += "!"; // error: cannot modify through reference to const
std::cout << r2 << '\n'; // prints s, which now holds "Example"
}Они также могут использоваться для реализации семантики передачи по ссылке в вызовах функций:
#include <iostream>
#include <string>
void double_string(std::string& s)
{
s += s; // 's' is the same object as main()'s 'str'
}
int main()
{
std::string str = "Test";
double_string(str);
std::cout << str << '\n';
}Когда тип возвращаемого значения функции — ссылка на lvalue, выражение вызова функции становится выражением lvalue:
#include <iostream>
#include <string>
char& char_number(std::string& s, std::size_t n)
{
return s.at(n); // string::at() returns a reference to char
}
int main()
{
std::string str = "Test";
char_number(str, 1) = 'a'; // the function call is lvalue, can be assigned to
std::cout << str << '\n';
}
Ссылки на rvalueСсылки на rvalue могут использоваться для расширения срока жизни временных объектов (обратите внимание, что ссылки на const lvalue также могут продлевать срок жизни временных объектов, но они не могут быть изменены через них): #include <iostream>
#include <string>
int main()
{
std::string s1 = "Test";
// std::string&& r1 = s1; // error: can't bind to lvalue
const std::string& r2 = s1 + s1; // okay: lvalue reference to const extends lifetime
// r2 += "Test"; // error: can't modify through reference to const
std::string&& r3 = s1 + s1; // okay: rvalue reference extends lifetime
r3 += "Test"; // okay: can modify through reference to non-const
std::cout << r3 << '\n';
}Более важно, когда у функции есть как перегрузки с ссылками на rvalue, так и на lvalue, перегрузка с ссылкой на rvalue связывается со значениями rvalue (включая prvalue и xvalue), а перегрузка с ссылкой на lvalue — со значениями lvalue: #include <iostream>
#include <utility>
void f(int& x)
{
std::cout << "lvalue reference overload f(" << x << ")\n";
}
void f(const int& x)
{
std::cout << "lvalue reference to const overload f(" << x << ")\n";
}
void f(int&& x)
{
std::cout << "rvalue reference overload f(" << x << ")\n";
}
int main()
{
int i = 1;
const int ci = 2;
f(i); // calls f(int&)
f(ci); // calls f(const int&)
f(3); // calls f(int&&)
// would call f(const int&) if f(int&&) overload wasn't provided
f(std::move(i)); // calls f(int&&)
// rvalue reference variables are lvalues when used in expressions
int&& x = 1;
f(x); // calls f(int& x)
f(std::move(x)); // calls f(int&& x)
}Это позволяет конструкторам перемещения, операторам присваивания перемещением и другим функциям, ориентированным на перемещение (например, Поскольку ссылки на rvalue могут связываться со значениями xvalue, они могут ссылаться на невременные объекты: int i2 = 42; int&& rri = std::move(i2); // binds directly to i2 Это позволяет выполнить перемещение из объекта в области видимости, который больше не нужен: std::vector<int> v{1, 2, 3, 4, 5};
std::vector<int> v2(std::move(v)); // binds an rvalue reference to v
assert(v.empty());Ссылки-перенаправленияСсылки-перенаправления — это особый вид ссылок, сохраняющий категорию значения аргумента функции, что позволяет перенаправить его с помощью template<class T>
int f(T&& x) // x is a forwarding reference
{
return g(std::forward<T>(x)); // and so can be forwarded
}
int main()
{
int i;
f(i); // argument is lvalue, calls f<int&>(int&), std::forward<int&>(x) is lvalue
f(0); // argument is rvalue, calls f<int>(int&&), std::forward<int>(x) is rvalue
}
template<class T>
int g(const T&& x); // x is not a forwarding reference: const T is not cv-unqualified
template<class T>
struct A
{
template<class U>
A(T&& x, U&& y, int* p); // x is not a forwarding reference: T is not a
// type template parameter of the constructor,
// but y is a forwarding reference
};auto&& за исключением случаев, когда вывод происходит из списка инициализаторов в фигурных скобках: auto&& vec = foo(); // foo() may be lvalue or rvalue, vec is a forwarding reference
auto i = std::begin(vec); // works either way
(*i)++; // works either way
g(std::forward<decltype(vec)>(vec)); // forwards, preserving value category
for (auto&& x: f())
{
// x is a forwarding reference; this is a common way to use range for in generic code
}
auto&& z = {1, 2, 3}; // *not* a forwarding reference (special case for initializer lists)См. также вывод аргументов шаблонов и |
(с C++11) |
Висячие ссылки
Хотя ссылки, после инициализации, всегда ссылаются на допустимые объекты или функции, возможно создание программы, где срок жизни объекта, на который ссылается ссылка, заканчивается, но ссылка остаётся доступной (висячая). Обращение к такой ссылке — это неопределённое поведение. Типичный пример — функция, возвращающая ссылку на автоматическую переменную:
std::string& f()
{
std::string s = "Example";
return s; // exits the scope of s:
// its destructor is called and its storage deallocated
}
std::string& r = f(); // dangling reference
std::cout << r; // undefined behavior: reads from a dangling reference
std::string s = f(); // undefined behavior: copy-initializes from a dangling referenceОбратите внимание, что ссылки на rvalue и ссылки на const lvalue продлевают срок жизни временных объектов (см. Инициализация ссылок для правил и исключений).
Если объект, на который ссылается ссылка, был уничтожен (например, явным вызовом деструктора), но память не была освобождена, к объекту за пределами срока жизни можно обращаться ограниченными способами, и ссылка может стать действительной, если объект будет вновь создан в той же памяти (см. Доступ вне срока жизни для подробностей).
Примечания
| Макрос проверки поддержки функции | Значение | Std | Функция |
|---|---|---|---|
__cpp_rvalue_references |
200610L | (C++11) | Ссылки на rvalue |
Отчёты об ошибках
Следующие отчёты об ошибках, изменяющие поведение, были применены ретроактивно к ранее опубликованным стандартам C++.
| DR | Применено к | Поведение, опубликованное | Корректное поведение |
|---|---|---|---|
| CWG 1510 | C++11 | квалифицированные cv ссылки не могли быть сформированы в операнде decltype |
разрешено |
| CWG 2550 | C++98 | параметры могли иметь тип «ссылка на void» | теперь разрешено |
Внешние ссылки
| Thomas Becker, 2013 - C++ Rvalue References Explained |
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/cpp/language/reference