Инициализация ссылок
Связывает ссылку с объектом.
Синтаксис
Инициализация без списков
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. После инициализации ссылку нельзя переназначить (изменить), чтобы она ссылалась на другой объект.
Ссылки инициализируются в следующих ситуациях:
return, когда функция возвращает тип ссылки.Объяснение
| 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как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, к нему применяется материализация временного объекта. Ссылка связывается с результирующим объектом или с соответствующим подобъектом базового класса. | (начиная с 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, его тип | (начиная с C++17) |
Непрямая привязка
Если прямая привязка недоступна, рассматривается непрямая привязка. В этом случае T не может быть ссылкой на U.
Если T или U — это тип класса, рассматриваются пользовательские преобразования в соответствии с правилами копирующей инициализации объекта типа T с помощью пользовательского преобразования. Программа является некорректной, если соответствующая копирующая инициализация без ссылок была бы некорректной. Затем результат вызова функции преобразования, как описано для копирующей инициализации без ссылок, используется для прямой инициализации ссылки. Для этой прямой инициализации пользовательские преобразования не рассматриваются.
| В противном случае создается временный объект типа | (до C++17) |
| В противном случае target неявно преобразуется в prvalue типа «неквалифицированный | (начиная с 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— одно из этих выражений.
Существуют следующие исключения из этого правила о сроке жизни:
- временный объект, привязанный к значению возврата функции в операторе return, не продлевается: он уничтожается сразу по окончании выражения возврата. Такой оператор return всегда возвращает висячую ссылку.
- временный объект, привязанный к параметру-ссылке в вызове функции, существует до конца всего выражения, содержащего этот вызов функции: если функция возвращает ссылку, которая сохраняется дольше всего выражения, она становится висячей ссылкой.
| (начиная с 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 issue 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 класса | разрешено |
| CWG 656 | C++98 | ссылка с квалификатором const, инициализированная типом, который не совместим со ссылкой, но имеет функцию преобразования в тип, совместимый со ссылкой, была привязана к временной переменной, скопированной из возвращаемого значения (или его подобъекта базового класса) функции преобразования | привязана к возвращаемому значению (или его подобъекту базового класса) напрямую |
| CWG 1287 | C++11 | преобразование из целевого типа класса в другой совместимый со ссылкой тип могло быть только неявным | разрешить явные преобразования |
| CWG 1295 | C++11 | ссылка могла быть привязана к битовому полю xvalue | запрещено |
| CWG 1299 | C++98 | определение временной переменной было неясным | уточнено |
| CWG 1571 | C++98 | пользовательские преобразования в косвенной связывании не учитывали тип целевого | учтены |
| CWG 1604 | C++98 | пользовательские преобразования не учитывались в косвенной связывании | учтены |
| CWG 2352 | C++98 | совместимость ссылок не учитывала преобразования квалификаций | учтены |
| CWG 2481 | C++17 | cv-квалификация не была добавлена к типу результата материализации временной переменной в косвенной связывании | добавлены |
| CWG 2801 | C++98 | типы, связанные со ссылками, были разрешены для косвенной связки | запрещены |
См. также
- конструктор
- конструктор преобразования
- копирующая операция присваивания
- копирующий конструктор
-
explicit - инициализация
- перемещающая операция присваивания
- перемещающий конструктор
-
new
© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
https://en.cppreference.com/w/cpp/language/reference_initialization