Инициализация ссылки
Привязывает ссылку к объекту.
Синтаксис
Инициализация не списком
T & ref = target ;T | (1) | |
T && ref = target ;T | (2) | (начиная с C++11) |
func-refpar ( target ) | (3) | |
return target ; | (4) | (в определении func-refret ) |
Class::Class(...) : ref-member ( target ) { ... } | (5) | (в определении Class ) |
Обычная списочная инициализация (начиная с C++11)
T & ref = { arg1, arg2, ... };T | (1) | |
T && ref = { arg1, arg2, ... };T | (2) | |
func-refpar ({ arg1, arg2, ... }); | (3) |
Списочная инициализация с указанием полей (начиная с C++20)
T & ref = {.des1 = arg1 , .des2 { arg2 } ... };T | (1) | |
T && ref = {.des1 = arg1 , .des2 { arg2 } ... };T | (2) | |
func-refpar ({.des1 = arg1 , .des2 { arg2 } ... }); | (3) |
Ссылка на T может быть инициализирована объектом типа T, функцией типа T или объектом, неявно преобразуемым к T. После инициализации ссылку нельзя переназначить (изменить так, чтобы она ссылалась на другой объект).
Ссылки инициализируются в следующих ситуациях:
Пояснение
| T | - | тип, на который ссылаются |
| ref | - | инициализируемая переменная-ссылка |
| target | - | используемое выражение-инициализатор |
| func-refpar | - | функция с параметром ссылочного типа (T & или T &&(начиная с C++11)) |
| func-refret | - | функция, возвращаемый тип которой является ссылочным (T & или T &&(начиная с C++11)) |
| Class | - | имя класса |
| ref-member | - | нестатический член данных ссылочного типа (T & или T &&(начиная с C++11)) класса Class |
| des1, des2, ... | - | обозначения полей |
| arg1, arg2, ... | - | инициализаторы в списках инициализации |
Определения
Для двух типов T1 и T2:
- Если взять версии
T1иT2без cv-квалификаторов, обозначив их соответственно какU1иU2, то при условии, чтоU1сходен сU2илиU1является базовым классом дляU2,T1связан по ссылке сT2. - Если prvalue типа «указатель на
T2» может быть преобразован к типу «указатель наT1» посредством стандартной последовательности преобразований,T1совместим по ссылке сT2.
Правила инициализации
| Если при инициализации ссылки используется обычная или указанная(начиная с C++20) списочная инициализация, применяются правила списочной инициализации. | (начиная с C++11) |
При инициализации ссылки не списком, если тип target обозначить как U, ссылка либо привязывается напрямую к target, либо привязывается к значению типа T, полученному преобразованием target. Сначала рассматривается прямая привязка, а затем косвенная. Если ни один вариант привязки невозможен, программа некорректна.
Во всех случаях, когда отношение совместимости по ссылке между двумя типами используется для проверки допустимости привязки ссылки, а стандартная последовательность преобразований была бы некорректной, программа, требующая такой привязки, некорректна.
Прямая привязка
Если выполняются все следующие условия:
- Инициализируемая ссылка является lvalue-ссылкой.
- target является lvalue, не являющимся битовым полем.
-
Tсовместим по ссылке сU.
Тогда ссылка привязывается к target или к соответствующему подобъекту базового класса:
double d = 2.0;
double& rd = d; // rd refers to d
const double& rcd = d; // rcd refers to d
struct A {};
struct B : A {} b;
A& ra = b; // ra refers to A subobject in b
const A& rca = b; // rca refers to A subobject in b
В противном случае, если выполняются все следующие условия:
- Инициализируемая ссылка является lvalue-ссылкой.
-
Uявляется типом класса. -
Tне связан по ссылке сU. -
target можно преобразовать в lvalue типа
Vтак, чтоTсовместим по ссылке сV.
Тогда ссылка привязывается к lvalue-результату преобразования или к соответствующему подобъекту базового класса:
struct A {};
struct B : A { operator int&(); };
int& ir = B(); // ir refers to the result of B::operator int&
В противном случае, если инициализируемая ссылка является lvalue-ссылкой, а T не имеет квалификатора const или имеет квалификатор volatile, программа некорректна:
double& rd2 = 2.0; // error: not an lvalue and reference is not const int i = 2; double& rd3 = i; // error: type mismatch and reference is not const
В противном случае, если выполняются все следующие условия:
- target является значением одной из следующих категорий:
| (до C++11) |
|
(начиная с C++11) (до C++17) |
| (начиная с C++17) |
-
Tсовместим по ссылке сU.
Тогда ссылка привязывается к target или к соответствующему подобъекту базового класса:
struct A {};
struct B : A {};
extern B f();
const A& rca2 = f(); // bound to the A subobject of the B rvalue.
A&& rra = f(); // same as above
int i2 = 42;
int&& rri = static_cast<int&&>(i2); // bound directly to i2
| Если target является prvalue, к нему применяется материализация временного объекта, при этом тип prvalue считается скорректированным типом
В этом случае ссылка привязывается к результирующему объекту или к соответствующему подобъекту базового класса. | (начиная с C++17) |
В противном случае, если выполняются все следующие условия:
-
Uявляется типом класса. -
Tне связан по ссылке сU. -
target можно преобразовать в значение
vтипаVтак, чтоTсовместим по ссылке сV, причемvотносится к одной из следующих категорий:
| (до C++11) |
|
(начиная с C++11) (до C++17) |
| (начиная с C++17) |
Затем ссылка связывается с результатом преобразования или с соответствующим подобъектом базового класса:
struct A {};
struct B : A {};
struct X { operator B(); } x;
const A& r = x; // bound to the A subobject of the result of the conversion
B&& rrb = x; // bound directly to the result of the conversion
| Если результат преобразования — prvalue, к нему применяется материализация временного объекта; при этом тип prvalue считается скорректированным типом
В этом случае ссылка связывается с результирующим объектом или с соответствующим подобъектом базового класса. | (начиная с C++17) |
Косвенное связывание
Если прямое связывание недоступно, рассматривается косвенное связывание. В этом случае T не может быть связано по ссылке с U.
Если T или U имеет тип класса, пользовательские преобразования рассматриваются согласно правилам копирующей инициализации объекта типа T посредством пользовательского преобразования. Программа некорректна, если соответствующая копирующая инициализация без ссылки была бы некорректной. Затем результат вызова функции преобразования, как описано для копирующей инициализации без ссылки, используется для прямой инициализации ссылки. При этой прямой инициализации пользовательские преобразования не рассматриваются.
| В противном случае создается временный объект типа | (до C++17) |
| В противном случае target неявно преобразуется в prvalue типа «cv-неквалифицированный | (начиная с C++17) |
const std::string& rs = "abc"; // rs refers to temporary copy-initialized from char array const double& rcd2 = 2; // rcd2 refers to temporary with value 2.0 int i3 = 2; double&& rrd3 = i3; // rrd3 refers to temporary with value 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— одно из этих выражений.
Из этого правила о времени жизни существуют следующие исключения:
| (до C++26) |
- временный объект, связанный с параметром-ссылкой при вызове функции, существует до конца полного выражения, содержащего этот вызов функции: если функция возвращает ссылку, время жизни которой превышает время жизни полного выражения, она становится висячей ссылкой.
| (начиная с C++11) |
struct A
{
int&& r;
};
A a1{7}; // OK, lifetime is extended
A a2(7); // well-formed, but dangling reference
| (начиная с C++20) |
В общем случае время жизни временного объекта нельзя продлить дальше, «передав его»: вторая ссылка, инициализированная из переменной-ссылки или члена данных, с которым связан временный объект, не влияет на время его жизни.
Примечания
Ссылки указываются без инициализаторов только в объявлении параметра функции, в объявлении типа возвращаемого значения функции, в объявлении члена класса и со спецификатором extern.
До принятия решения по вопросу CWG 1696 временный объект разрешалось связывать с членом-ссылкой в списке инициализаторов конструктора; он существовал только до выхода из конструктора, а не столько же, сколько сам объект. Такая инициализация некорректна начиная с CWG 1696, хотя многие компиляторы по-прежнему ее поддерживают (заметное исключение — clang).
Пример
#include <sstream>
#include <utility>
struct S
{
int mi;
const std::pair<int, int>& mp; // reference member
};
void foo(int) {}
struct A {};
struct B : A
{
int n;
operator int&() { return n; }
};
B bar() { return B(); }
//int& bad_r; // error: no initializer
extern int& ext_r; // OK
int main()
{
// Lvalues
int n = 1;
int& r1 = n; // lvalue reference to the object n
const int& cr(n); // reference can be more cv-qualified
volatile int& cv{n}; // any initializer syntax can be used
int& r2 = r1; // another lvalue reference to the object n
// int& bad = cr; // error: less cv-qualified
int& r3 = const_cast<int&>(cr); // const_cast is needed
void (&rf)(int) = foo; // lvalue reference to function
int ar[3];
int (&ra)[3] = ar; // lvalue reference to array
B b;
A& base_ref = b; // reference to base subobject
int& converted_ref = b; // reference to the result of a conversion
// Rvalues
// int& bad = 1; // error: cannot bind lvalue ref to rvalue
const int& cref = 1; // bound to rvalue
int&& rref = 1; // bound to rvalue
const A& cref2 = bar(); // reference to A subobject of B temporary
A&& rref2 = bar(); // same
int&& xref = static_cast<int&&>(n); // bind directly to n
// int&& copy_ref = n; // error: can't bind to an lvalue
double&& copy_ref = n; // bind to an rvalue temporary with value 1.0
// Restrictions on temporary lifetimes
// std::ostream& buf_ref = std::ostringstream() << 'a';
// the ostringstream temporary was bound to the left operand
// of operator<< but its lifetime ended at the semicolon so
// the buf_ref is a dangling reference
S a {1, {2, 3}}; // temporary pair {2, 3} bound to the reference member
// a.mp and its lifetime is extended to match
// the lifetime of object a
S* p = new S{1, {2, 3}}; // temporary pair {2, 3} bound to the reference
// member p->mp, but its lifetime ended at the semicolon
// p->mp is a dangling reference
delete p;
// Imitate [[maybe_unused]] applied to the following variables:
[](...){}
(
cv, r2, r3, rf, ra, base_ref, converted_ref,
a, cref, rref, cref2, rref2, copy_ref, xref
);
}
Отчеты об ошибках
Следующие отчеты об ошибках, изменяющие поведение, были применены ретроактивно к ранее опубликованным стандартам C++.
| DR | Применяется к | Поведение согласно опубликованной редакции | Правильное поведение |
|---|---|---|---|
| CWG 391 | C++98 | При инициализации ссылки на тип с квалификатором const объектом типа класса rvalue мог создаваться временный объект; чтобы скопировать rvalue во временный объект, требовался конструктор этого класса | временный объект не создается, конструктор не требуется |
| CWG 450 | C++98 | ссылку на массив с квалификатором const нельзя было инициализировать совместимым по ссылке rvalue-массивом | разрешено |
| CWG 589 | C++98 | ссылку нельзя было напрямую связать с rvalue-массивом или rvalue класса | разрешено |
| CWG 656 | C++98 | ссылка на тип с квалификатором const, инициализированная типом, который не является совместимым по ссылке, но имеет функцию преобразования в совместимый по ссылке тип, связывалась с временным объектом, скопированным из возвращаемого значения (или подобъекта базового класса) функции преобразования | напрямую связывалась с возвращаемым значением (или его подобъектом базового класса) |
| CWG 1287 | C++11 | преобразование target типа класса в другой совместимый по ссылке тип могло быть только неявным | разрешены явные преобразования |
| CWG 1295 | C++11 | ссылка могла связываться с xvalue битового поля | запрещено |
| CWG 1299 | C++98 | определение временного объекта было неясным | прояснено |
| CWG 1571 | C++98 | при пользовательских преобразованиях для косвенного связывания не учитывался тип target | учитывается |
| CWG 1604 | C++98 | пользовательские преобразования не учитывались при косвенном связывании | учитываются |
| CWG 2352 | C++98 | при проверке совместимости ссылок не учитывались преобразования квалификации | учитываются |
| CWG 2481 | C++17 | cv-квалификация не добавлялась к результирующему типу материализации временного объекта при косвенном связывании | добавлена |
| CWG 2657 | C++17 | cv-квалификация не добавлялась к результирующему типу материализации временного объекта при прямом связывании | добавлена |
| CWG 2801 | C++98 | для косвенного связывания допускались типы, связанные отношением reference-related | запрещено |
См. также
- конструктор
- преобразующий конструктор
- оператор копирующего присваивания
- конструктор копирования
explicit- инициализация
- оператор перемещающего присваивания
- конструктор перемещения
new
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/cpp/language/reference_initialization