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

Инициализация ссылки

Материал из cppreference.com
< cpp‎ | language
 
 
 
 

Связывает ссылку с объектом.

Содержание

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

T&ссылка=цель;

T&ссылка= {арг1,арг2, ... };

T&ссылка={.des1=арг1, .des2{арг2}...};

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

T&ссылка(цель);

T&ссылка{арг1,арг2, ... };

T&ссылка{.des1=арг1, .des2{арг2}...};

(начиная с C++20)
(1)

T&&ссылка=цель;

T&&ссылка= {арг1,арг2, ... };

T&&ссылка={.des1=арг1, .des2{арг2}...};

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

T&&ссылка(цель);

T&&ссылка{арг1,арг2, ... };

T&&ссылка{.des1=арг1, .des2{арг2}...};

(начиная с C++20)
(2) (начиная с C++11)
данная Rфункция(T&арг); или Rфункция(T&&арг);(начиная с C++11)

функция(цель)

функция({арг1,арг2, ... });

fn({.des1=арг1, .des2{арг2}...});

(начиная с C++20)
(3)
внутри T&функция() или T&&функция()(начиная с C++11)

returnцель;

(4)
данная T&ссылка; или T&&ссылка;(начиная с C++11) внутри определения Класс

Класс::Класс(...) :ссылка(цель) { ... }

(5)

[править]Объяснение

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

Ссылки инициализируются в следующих ситуациях:

1) Когда именованная переменная ссылка lvalue объявляется с помощью инициализатора
2) Когда именованная переменная ссылка rvalue объявлена с помощью инициализатора
3) В выражении вызова функции, когда параметр функции имеет ссылочный тип
4) В операторе return, когда функция возвращает ссылочный тип
5) Когда нестатический элемент данных ссылочного типа инициализируется с помощью инициализатора элемента

Эффекты инициализации ссылки:

  • Иначе, если ссылка является ссылкой lvalue:
  • Если цель является выражением lvalue и его типом является T или производный от T, и имеет такую же или меньшую cv-квалификацию, тогда ссылка привязывается к объекту, идентифицированного 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).
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

[править]Время жизни временного объекта

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

  • выражение prvalue типа объекта,
(до C++17)
(начиная с C++17)

Есть следующие исключения из этого правила времени жизни:

  • временная привязка к возвращаемому значению функции в операторе return не продлевается: она уничтожается сразу в конце возвращаемого выражения. Такой оператор return всегда возвращает висячую ссылку.
  • временная привязка к ссылочному параметру в вызове функции существует до конца полного выражения, содержащего этот вызов функции: если функция возвращает ссылку, которая пережила полное выражение, она становится висячей ссылкой.
  • временная привязка к ссылке в инициализаторе, используемом в выражении new, существует до конца полного выражения, содержащего это выражение new, а не до тех пор, пока инициализируется объект. Если инициализированный объект переживает полное выражение, его ссылочный элемент становится оборванной ссылкой.
(начиная с 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 определение временного объекта было неясным сделано ясным

[править]Смотрите также

close