Инициализация ссылки
Связывает ссылку с объектом.
Содержание |
[править]Синтаксис
T T
T T
| (1) | ||||||||
T T
T T
| (2) | (начиная с C++11) | |||||||
данная Rфункция( T& арг); или Rфункция( T&& арг); (начиная с C++11)функция функция
| (3) | ||||||||
внутри T& функция() или T&& функция() (начиная с C++11)
| (4) | ||||||||
данная T& ссылка; или T&& ссылка; (начиная с C++11) внутри определения КлассКласс | (5) | ||||||||
[править]Объяснение
Ссылка на T
может быть инициализирована объектом типа T
, функцией типа T
или объектом, неявно преобразуемым в T
. После инициализации ссылка не может быть переустановлена (изменена) чтобы ссылаться на другой объект.
Ссылки инициализируются в следующих ситуациях:
Эффекты инициализации ссылки:
- Если инициализатор представляет собой список инициализации в фигурных скобках (
{
арг1,
арг2,
...}
или{.
des1=
арг1, .
des2{
арг2}
...}
(начиная с C++20)), соблюдаются правила инициализации списком.
- Иначе, если ссылка является ссылкой lvalue:
- Если цель является выражением lvalue и его типом является
T
или производный отT
, и имеет такую же или меньшую cv-квалификацию, тогда ссылка привязывается к объекту, идентифицированного lvalue или к его подобъекту базового класса.
- Если цель является выражением lvalue и его типом является
double d =2.0;double& rd = d;// rd ссылается на dconstdouble& rcd = d;// rcd ссылается на d struct A {};struct B : A {} b; A& ra = b;// ra ссылается на A подобъект bconst A& rca = b;// rca ссылается на A подобъект b
- Иначе, если тип цели не тотже или не производный тип от
T
, и цель имеет функцию преобразования в lvalue типаT
или производного отT
, равного или менее cv-квалифицированного, тогда ссылка привязывается к объекту, идентифицированному значением lvalue, возвращаемым функцией преобразования (или к его подобъекту базового класса).
- Иначе, если тип цели не тотже или не производный тип от
struct A {};struct B : A { operator int&();}; int& ir = B();// ir ссылается на результат B::operator int&
- Иначе, если ссылка является ссылкой lvalue на не-volatile const-квалифицированный тип или ссылкой на rvalue(начиная с C++11):
- Если цель не является битовым полем rvalue или функцией lvalue, и его тип либо
T
, либо производный отT
, такой же или меньшей cv-квалификации, то ссылка привязывается к значению выражения инициализатора или к его базовому подобъекту (после материализации временного объекта, если необходимо)(начиная с C++17).
- Если цель не является битовым полем rvalue или функцией lvalue, и его тип либо
struct A {};struct B : A {};extern B f(); const A& rca2 = f();// привязано к подобъекту A значения B rvalue. A&& rra = f();// то же, что и выше int i2 =42;int&& rri =static_cast<int&&>(i2);// привязано непосредственно к i2
- Иначе, если тип цели не совпадает с типом
T
или производным от него, а цель имеет функцию преобразования в rvalue или функцию lvalue, тип которойT
или производный отT
, с равной или меньшей cv-квалификацией, то ссылка привязывается к результату функции преобразования или к её подобъекту базового класса (после материализации временного объекта, если необходимо)(начиная с C++17).
- Иначе, если тип цели не совпадает с типом
struct A {};struct B : A {};struct X { operator B();} x; const A& r = x;// привязывается к подобъекту A результата преобразования B&& rrb = x;// привязывается непосредственно к результату преобразования
- Иначе, цель неявно преобразуется в
T
. Ссылка привязывается к результату преобразования (после материализации временного объекта cv-квалификацияT
сохраняется, даже если это скалярный тип)(начиная с C++17). Если цель (или, если преобразование выполняется определяемым пользователем преобразованием, результат функции преобразования) имеет типT
или производного отT
, она должна иметь такую же или меньшую cv-квалификацию, и, если ссылка является ссылкой на rvalue, не должна быть lvalue(начиная с C++11).
- Иначе, цель неявно преобразуется в
conststd::string& rs ="abc";// rs ссылается на временный объект инициализированный// копией из массива символовconstdouble& rcd2 =2;// rcd2 ссылается на временный объект со значением 2.0int i3 =2;double&& rrd3 = i3;// rrd3 ссылается на временный объект со значением 2.0
[править]Время жизни временного объекта
Всякий раз, когда ссылка привязывается к временному объекту или его подобъекту, время жизни временного объекта продлевается, чтобы соответствовать времени жизни ссылки (проверяются исключения времени жизни временного объекта), где временный объект или его подобъект обозначается одним из следующих выражений:
| (до C++17) |
(начиная с C++17) |
- выражение в скобках (e), где e одно из этих выражений,
- встроенное выражение индексации вида a[n] или n[a], где a массив и одно из этих выражений,
- выражение доступа к элементу класса формы e.m, где e одно из этих выражений, а m обозначает нестатический элемент данных объектного типа,
- операция указателя на элемент формы e.*mp, где e одно из этих выражений, а mp — указатель на элемент данных,
- преобразование
const_cast
,static_cast
,dynamic_cast
илиreinterpret_cast
без пользовательского преобразования, которое преобразует одно из этих выражений в значение glvalue, ссылающееся на объект, обозначенный операндом, или на его полный объект, или его подобъект (выражение явного приведения интерпретируется как последовательность этих приведений), - условное выражение вида cond ? e1 : e2, которое является значением glvalue, где e1 или e2 являются одним из этих выражений, или
- встроенное выражение запятой в форме x, e, которое является значением glvalue, где e одно из этих выражений.
Есть следующие исключения из этого правила времени жизни:
- временная привязка к возвращаемому значению функции в операторе return не продлевается: она уничтожается сразу в конце возвращаемого выражения. Такой оператор return всегда возвращает висячую ссылку.
- временная привязка к ссылочному параметру в вызове функции существует до конца полного выражения, содержащего этот вызов функции: если функция возвращает ссылку, которая пережила полное выражение, она становится висячей ссылкой.
| (начиная с C++11) |
struct A {int&& r;}; A a1{7};// OK, время жизни продлевается A a2(7);// правильно, но ссылка является оборваной | (начиная с C++20) |
Как правило, время жизни временного объекта не может быть продлено путём его "передачи": вторая ссылка, инициализированная из ссылочной переменной или элемента данных, с которым был связан временный объект, не влияет на его время жизни.
[править]Примечание
Ссылки появляются без инициализаторов только в объявлении параметра функции, в объявлении типа возвращаемого значения функции, в объявлении элемента класса и со спецификатором extern
.
До разрешения CWG проблема 1696, временному объекту разрешалось привязываться к элементу ссылке в конструкторе со списком инициализации, и она сохранялась только до выхода из конструктора, а не до тех пор, пока существует объект. Такая инициализация является неправильной, начиная с CWG 1696, хотя многие компиляторы все ещё поддерживают её (заметным исключением является clang).
[править]Пример
#include <utility>#include <sstream> struct S {int mi;conststd::pair<int, int>& mp;// элемент ссылка}; void foo(int){} struct A {}; struct B : A {int n; operator int&(){return n;}}; B bar(){return B();} //int& bad_r; // ошибка: нет инициализатораexternint& ext_r;// OK int main(){// Lvalueint n =1;int& r1 = n;// lvalue ссылка на объект nconstint& cr(n);// ссылка может быть более cv-квалифицированнойvolatileint& cv{n};// можно использовать любой синтаксис инициализатораint& r2 = r1;// еще одна ссылка lvalue на объект n// int& bad = cr; // ошибка: менее cv-квалифицированаint& r3 =const_cast<int&>(cr);// нужен const_cast void(&rf)(int)= foo;// lvalue ссылка на функциюint ar[3];int(&ra)[3]= ar;// lvalue ссылка на массив B b; A& base_ref = b;// ссылка на базовый подобъектint& converted_ref = b;// ссылка на результат преобразования // Rvalue// int& bad = 1; // ошибка: невозможно привязать ссылку lvalue к rvalueconstint& cref =1;// привязка к rvalueint&& rref =1;// привязка к rvalue const A& cref2 = bar();// ссылка на подобъект A временного B A&& rref2 = bar();// тоже самое int&& xref =static_cast<int&&>(n);// привязка непосредственно к n// int&& copy_ref = n; // ошибка: невозможно привязать к lvaluedouble&& copy_ref = n;// привязка к временному значению rvalue// со значением 1.0 // Ограничения на время жизни временного объектаstd::ostream& buf_ref =std::ostringstream()<<'a';// временное значение// ostringstream привязывается к левому операнду operator<<,// но его время жизни заканчивается точкой с запятой,// поэтому buf_ref является оборванной ссылкой S a {1, {2, 3}};// временная пара {2, 3} привязанная к ссылке на элемент// a.mp, и её время жизни увеличивается, чтобы// соответствовать времени жизни объекта a S* p = new S{1, {2, 3}};// временная пара {2, 3} привязывается к ссылке на// элемент p->mp, но ёе время жизни заканчивается точкой// с запятой, p->mp является оборванной ссылкой delete p;}
[править]Отчёты о дефектах
Следующие изменения поведения были применены с обратной силой к ранее опубликованным стандартам C++:
Номер | Применён | Поведение в стандарте | Корректное поведение |
---|---|---|---|
CWG 391 | C++98 | инициализация ссылки на константный тип с типом класса rvalue может создать временное значение, и для копирования значения rvalue в это временное значение требуется конструктор этого класса. | временное значение не создаётся, конструктор не требуется |
CWG 450 | C++98 | ссылка на константный массив не может быть инициализирована массивом, совместимым со ссылкой rvalue | позволено |
CWG 656 | C++98 | ссылка на константный тип, инициализированный типом, который не является совместимым со ссылкой, но имеет функцию преобразования в совместимый со ссылкой тип, привязывается к временному значению, скопированному из возвращаемого значения (или его подобъекта базового класса) функции преобразования | привязывается к возвращаемому значению (или его подобъекту базового класса) напрямую |
CWG 1299 | C++98 | определение временного объекта было неясным | сделано ясным |