Spec-Zone.ru › C++

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

Привязывает ссылку к объекту.

Синтаксис

Инициализация не списком
T & ref = target ;

T & ref ( target );

(1)
T && ref = target ;

T && ref ( target );

(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 & ref { arg1, arg2, ... };

(1)
T && ref = { arg1, arg2, ... };

T && ref { arg1, arg2, ... };

(2)
func-refpar ({ arg1, arg2, ... }); (3)
Списочная инициализация с указанием полей (начиная с C++20)
T & ref = {.des1 = arg1 , .des2 { arg2 } ... };

T & ref {.des1 = arg1 , .des2 { arg2 } ... };

(1)
T && ref = {.des1 = arg1 , .des2 { arg2 } ... };

T && ref {.des1 = arg1 , .des2 { arg2 } ... };

(2)
func-refpar ({.des1 = arg1 , .des2 { arg2 } ... }); (3)

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

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

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

Пояснение

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 является значением одной из следующих категорий:
  • rvalue
(до C++11)
  • xvalue, не являющееся битовым полем
  • prvalue класса
  • prvalue массива
  • lvalue функции
(начиная с C++11)
(до C++17)
  • rvalue, не являющееся битовым полем
  • lvalue функции
(начиная с 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 считается скорректированным типом P.

  • P корректируется относительно типа target (то есть U) путем добавления к нему cv-квалификаторов T.

В этом случае ссылка привязывается к результирующему объекту или к соответствующему подобъекту базового класса.

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

В противном случае, если выполняются все следующие условия:

  • U является типом класса.
  • T не связан по ссылке с U.
  • target можно преобразовать в значение v типа V так, что T совместим по ссылке с V, причем v относится к одной из следующих категорий:
  • rvalue
(до C++11)
  • xvalue
  • class prvalue
  • function lvalue
(начиная с C++11)
(до C++17)
  • rvalue
  • function lvalue
(начиная с 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 считается скорректированным типом P.

  • P корректируется относительно типа результата преобразования путем добавления к нему cv-квалификации T.

В этом случае ссылка связывается с результирующим объектом или с соответствующим подобъектом базового класса.

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

Косвенное связывание

Если прямое связывание недоступно, рассматривается косвенное связывание. В этом случае T не может быть связано по ссылке с U.

Если T или U имеет тип класса, пользовательские преобразования рассматриваются согласно правилам копирующей инициализации объекта типа T посредством пользовательского преобразования. Программа некорректна, если соответствующая копирующая инициализация без ссылки была бы некорректной. Затем результат вызова функции преобразования, как описано для копирующей инициализации без ссылки, используется для прямой инициализации ссылки. При этой прямой инициализации пользовательские преобразования не рассматриваются.

В противном случае создается временный объект типа T и копирующе инициализируется из target. Затем ссылка связывается с временным объектом.

(до C++17)

В противном случае target неявно преобразуется в prvalue типа «cv-неквалифицированный T». Применяется преобразование материализации временного объекта; при этом тип prvalue считается T, а ссылка связывается с результирующим объектом.

(начиная с 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

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

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

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

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API