Пространства имён
Варианты
Действия

Объявление ссылки

Материал из cppreference.com
< cpp‎ | language
 
 
Язык С++
Общие темы
Управление потоком
Операторы условного выполнения
Операторы итерации (циклы)
Операторы перехода
Функции
Объявление функции
Выражение лямбда-функции
Спецификатор inline
Спецификации динамических исключений(до C++17*)
Спецификатор noexcept(C++11)
Исключения
Пространства имён
Типы
Спецификаторы
decltype(C++11)
auto(C++11)
alignas(C++11)
Спецификаторы длительности хранения
Инициализация
Выражения
Альтернативные представления
Литералы
Логические - Целочисленные - С плавающей запятой
Символьные - Строковые - nullptr(C++11)
Определяемые пользователем(C++11)
Утилиты
Атрибуты(C++11)
Types
Объявление typedef
Объявление псевдонима типа(C++11)
Casts
Неявные преобразования - Явные преобразования
static_cast - dynamic_cast
const_cast - reinterpret_cast
Выделение памяти
Классы
Свойства функции класса
Специальные функции-элементы
Шаблоны
Разное
 
Обьявления
 

Объявленная как ссылка именованная переменная, это псевдоним уже существующего объекта или функции.

Содержание

[править]Синтаксис

Объявление переменной ссылки, это любое простое объявление, которое имеет следующие формы:

&атрибуты (необязательно)декларатор (1)
&&атрибуты (необязательно)декларатор (2) (начиная с C++11)
1)Объявление ссылки на левостороннее значение: объявление S& D; объявляет D, как ссылку на левостороннее значение на тип, определённый как последовательность-спецификаторов-объявленияS.
2)Объявление ссылки на правостороннее значение: объявление S&& D; объявляет D, как ссылку на правостороннее значение на тип, определённый как последовательность-спецификаторов-объявленияS.
декларатор любое объявление, за исключением объявления другой ссылки (не существует ссылок на ссылки)
атрибуты(начиная с C++11) список атрибутов

Ссылка должна быть инициализирована, чтобы ссылаться на допустимый объект или функцию: смотрите инициализация ссылки.

Тип “ссылка на (возможно cv-квалифицированный) void” не может быть создана.

Ссылочные типы не могут быть квалифицированы как cv на самом верхнем уровне; не существует синтаксиса для такого объявления, и если квалификация добавлена к имени, определённом typedef, или спецификатору decltype,(начиная с C++11) или тип является параметром шаблона, она игнорируется.

Ссылки не являются объектами; они не обязательно занимают память, хотя компилятор может распределить память, если это необходимо, чтобы реализовать необходимую семантику (например нестатический элемент-данных ссылочного типа обычно увеличивает размер класса, как необходимо, чтобы хранить адрес памяти).

Так как ссылки не являются объектами, не существует массивов ссылок, указателей на ссылки, и ссылок на ссылки:

int& a[3];// ошибкаint&* p;// ошибкаint&&r;// ошибка

Сжатие ссылки

Не разрешено создавать ссылку на ссылку через манипуляцию типами в шаблонах или typedef’ах, в этом случае применяются правила сжатия ссылки: ссылка на правостороннее значение на ссылку на правостороннее значение сжимается в ссылку на правостороннее значение, все другие комбинации формируют ссылку на левостороннее значение:

typedefint& lref;typedefint&& rref;int n; lref& r1 = n;// тип переменной r1, это int& lref&& r2 = n;// тип переменной r2, это int& rref& r3 = n;// тип переменной r3, это int& rref&& r4 =1;// тип переменной r4, это int&&

(Это существует наряду со специальными правилами вывода аргументов шаблона, когда T&& используется в шаблоне функции, возможно формирование правил, которые делают возможным std::forward.)

(начиная с C++11)

[править]Ссылки на левостороннее значение

Ссылки на левостороннее значение могут использоваться для создания псевдонимов существующих объектов (опционально с различной cv квалификацией):

#include <iostream>#include <string>   int main(){std::string s ="Пр";std::string& r1 = s;conststd::string& r2 = s;   r1 +="имер";// изменяет s// r2 += "!"; // ошибка: невозможно изменить через ссылку на константуstd::cout<< r2 <<'\n';// выводит s, которая теперь содержит "Пример"}

Они могут также использоваться для реализации семантики передачи по ссылке в вызовах функций:

#include <iostream>#include <string>   void double_string(std::string& s){ s += s;// 's' тот же объект, как и 'str' в функции main()}   int main(){std::string str ="Тест"; 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() возвращает ссылку на символ}   int main(){std::string str ="Тест"; char_number(str, 1)='о';// вызов функции это lvalue, которое может быть измененоstd::cout<< str <<'\n';}

Ссылки на правостороннее значение

Ссылки на правостороннее значение могут использоваться для продления времени жизни временных объектов (обратите внимание, ссылки lvalue на константу также могут продлить время жизни временных объектов, но эти объекты не могут быть изменены через них):

#include <iostream>#include <string>   int main(){std::string s1 ="Тест";// std::string&& r1 = s1; // ошибка: невозможно связать с lvalue   conststd::string& r2 = s1 + s1;// окей: ссылка lvalue на константу продлевает// время жизни// r2 += "Тест"; // ошибка: невозможно модифицировать через ссылку на// константу   std::string&& r3 = s1 + s1;// окей: ссылка rvalue продлевает время жизни r3 +="Тест";// окей: изменение возможно через ссылку на// не константуstd::cout<< r3 <<'\n';}

Что ещё более важно, когда функция имеет перегрузки со ссылкой rvalue и ссылкой lvalue, перегрузка со ссылкой rvalue связывается со значениями rvalue (включая значения и prvalue и xvalue), в то время как перегрузка со ссылкой lvalue привязывается к значениям lvalue:

#include <iostream>#include <utility>   void f(int& x){std::cout<<"ссылка lvalue вызывает перегрузку f("<< x <<")\n";}   void f(constint& x){std::cout<<"ссылка lvalue на константу вызывает перегрузку f("<< x <<")\n";}   void f(int&& x){std::cout<<"ссылка rvalue вызывает перегрузку f("<< x <<")\n";}   int main(){int i =1;constint ci =2; f(i);// вызывает f(int&) f(ci);// вызывает f(const int&) f(3);// вызывает f(int&&)// будет вызвана f(const int&), если не предоставлена перегрузка f(int&&) f(std::move(i));// вызывает f(int&&)   // ссылочные переменные rvalue являются значениями lvalue, когда используются// в выраженияхint&& x =1; f(x);// вызывает f(int& x) f(std::move(x));// вызывает f(int&& x)}

Это позволяет создавать конструкторы перемещения, операторы присваивания перемещением, и другие функции, учитывающие перемещение, (например std::vector::push_back()), которые автоматически выбираются при необходимости.

Так как ссылки на rvalue могут привязываться к xvalue, они могут ссылаться на невременный объект:

int i2 =42;int&& rri = std::move(i2);// связывается непосредственно с i2

Это позволяет переместить объект из области видимости, когда он больше не нужен:

std::vector<int> v{1,2,3,4,5};std::vector<int> v2(std::move(v));// связывает ссылку на rvalue с vassert(v.empty());
(начиная с C++11)

Пересылаемые ссылки

Пересылаемые ссылки, это специальный вид ссылок, который представляет категорию значения аргумента функции, что позволяет переслать его с помощью std::forward. Пересылаемые ссылки, это одно из следующего:

1) параметр функции шаблона, объявленный как ссылка на правостороннее значение на cv-неквалифицированный тип параметра шаблона того же шаблона функции:
template<class T>int f(T&& x){// x, это пересылаемая ссылкаreturn g(std::forward<T>(x));// и так будет переслана}   int main(){int i; f(i);// аргумент, это lvalue, вызывается f<int&>(int&), std::forward<int&>(x),// это lvalue f(0);// аргумент, это rvalue, вызывается f<int>(int&&), std::forward<int>(x),// это rvalue}   template<class T>int g(const T&& x);// x, не пересылаемая ссылка: const T не является// cv-неквалифицированным   template<class T>struct A {template<class U> A(T&& x, U&& y, int* p);// x, не пересылаемая ссылка: T не является// типом параметра шаблона конструктора,// а y является пересылаемой переменной};
2)auto&&, за исключением случаев, когда выводится из заключённого в скобки списка инициализаторов:
auto&& vec = foo();// foo() может быть lvalue или rvalue, vec является пересылаемой// ссылкойauto i =std::begin(vec);// работает в любом случае(*i)++;// работает в любом случае g(std::forward<decltype(vec)>(vec));// пересылается, сохраняя категорию значения   for(auto&& x: f()){// x является пересылаемой ссылкой;// это распространённый способ использования диапазона в универсальном коде// для циклов}   auto&& z ={1, 2, 3};// *не* пересылаемая ссылка (особый случай для списков// инициализаторов)

Смотрите также вывод аргумента шаблона и std::forward.

(начиная с C++11)

[править]Висячие ссылки

Хотя ссылки, однажды инициализированные, всегда ссылаются на допустимые объекты или функции, можно создать программу, в которой время жизни упомянутого объекта заканчивается, но ссылка остаётся доступной (висячей). Доступ к такой ссылке имеет неопределённое поведение. Распространённый пример функции, возвращающей ссылку на автоматическую переменную:

std::string& f(){std::string s ="Пример";return s;// выход из области видимости s:// вызывается её деструктор и освобождается её память}   std::string& r = f();// висячая ссылкаstd::cout<< r;// неопределённое поведение: чтение из висячей ссылкиstd::string s = f();// неопределённое поведение: инициализация копированием// из висячей ссылки

Обратите внимание, что ссылки rvalue и ссылки lvalue на константу продлевают время жизни временных объектов (смотрите правила и исключения Инициализации ссылки).

Если объект, на который есть ссылка, был уничтожен (например, явным вызовом деструктора), но память не была освобождена, ссылка на объект с законченным временем жизни, может использоваться ограниченно, и может стать действительной, если объект будет воссоздан в той же области памяти (подробную информацию смотрите в Доступ вне времени жизни).

[править]Примечание

Макрос Тестирования функциональностиЗначениеСтандартФункциональность
__cpp_rvalue_references200610L(C++11)Ссылки на правостороннее значение

[править]Отчёты о дефектах

Следующие изменения поведения были применены с обратной силой к ранее опубликованным стандартам C++:

Номер Применён Поведение в стандарте Корректное поведение
CWG 1510 C++11 cv-квалифицированные ссылки не могут быть сформированы в операнде decltype допускается
CWG 2550 C++98 параметры могут иметь тип “ссылка на void сейчас разрешено

[править]Внешние ссылки

 Thomas Becker, 2013 - Объяснение Ссылок на Rvalue в C++
close