Namespaces
Variants

Reference initialization

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

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

Содержание

Синтаксис

Несписочная инициализация
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)
**Примечание:** В данном HTML-коде отсутствует текст для перевода, требующий локализации на русский язык. Весь представленный контент состоит из: - HTML-тегов и атрибутов (сохранены без изменений) - C++ кода внутри тегов ` ` (сохранен без изменений) - Символов и синтаксических конструкций C++ - Нумерации (1), (2), (3) Содержимое тегов ` ` представляет собой имена переменных и функций в C++, которые не подлежат переводу согласно требованиям.
Инициализация списком с указанием элементов (начиная с 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)
**Примечание**: В данном случае весь текст внутри тегов ` ` представляет собой C++ специфические термины и синтаксические конструкции, которые согласно инструкциям не подлежат переводу. HTML разметка и код внутри тегов ` ` также сохранены без изменений.

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

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

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

Объяснение

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 :

  • Для неквалифицированных cv-версий T1 и T2 , обозначаемых как U1 и U2 соответственно, если U1 является подобным для U2 , или U1 является базовым классом для U2 , то T1 является ссылочно-связанным с T2 .
  • Если prvalue типа «указатель на T2 » может быть преобразовано в тип «указатель на T1 » с помощью стандартной последовательности преобразований, то T1 является ссылочно-совместимым с T2 .

Правила инициализации

Если инициализация ссылки использует обычную или назначенную (начиная с C++20) list-инициализацию, применяются правила list-инициализации .

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

Для несписочной инициализации ссылки, при заданном типе target как U , ссылка либо связывается напрямую с target , либо связывается со значением типа T , преобразованным из target . Сначала рассматривается прямое связывание, затем косвенное; если ни один из вариантов связывания недоступен, программа является некорректной.

Во всех случаях, когда совместимое по ссылке отношение двух типов используется для установления действительности привязки ссылки, и стандартная последовательность преобразований была бы некорректно сформирована, программа, требующая такой привязки, является некорректно сформированной.

Прямое связывание

Если выполняются все следующие условия:

  • Инициализируемая ссылка является lvalue-ссылкой.
  • target является не- битовым полем lvalue.
  • T является ссылочно-совместимым с U .

Затем ссылка связывается с target или с соответствующим подобъектом базового класса:

double d = 2.0;
double& rd = d;        // rd ссылается на d
const double& rcd = d; // rcd ссылается на d
struct A {};
struct B : A {} b;
A& ra = b;             // ra ссылается на подобъект A в b
const A& rca = b;      // rca ссылается на подобъект A в b

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

  • Инициализируемая ссылка является lvalue-ссылкой.
  • U является типом класса.
  • T не является ссылочно-связанным с U .
  • target может быть преобразован в lvalue типа V таким образом, что T является ссылочно-совместимым с V .

Затем ссылка связывается с lvalue-результатом преобразования, или с его соответствующим базовым подобъектом класса:

struct A {};
struct B : A { operator int&(); };
int& ir = B(); // ir ссылается на результат B::operator int&

В противном случае, если инициализируемая ссылка является lvalue-ссылкой, и T не является const-квалифицированной или является volatile-квалифицированной, программа является некорректной:

double& rd2 = 2.0; // ошибка: не lvalue и ссылка не const
int i = 2;
double& rd3 = i;   // ошибка: несоответствие типов и ссылка не const

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

  • target — это значение любой из следующих категорий:
  • rvalue
(до C++11)
  • non-bit-field xvalue
  • class prvalue
  • array prvalue
  • function lvalue
(начиная с C++11)
(до C++17)
  • non-bit-field rvalue
  • function lvalue
(начиная с C++17)
  • T является ссылочно-совместимым с U .

Затем ссылка связывается с target , или с соответствующим подобъектом базового класса:

struct A {};
struct B : A {};
extern B f();
const A& rca2 = f(); // привязывается к подобъекту A rvalue типа B
A&& rra = f();       // то же самое, что и выше
int i2 = 42;
int&& rri = static_cast<int&&>(i2); // привязывается напрямую к i2

Если target является prvalue, материализация временного объекта применяется к нему, при этом тип prvalue считается скорректированным типом P .

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

(начиная с 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; // привязывается к подобъекту A результата преобразования
B&& rrb = x;    // привязывается напрямую к результату преобразования

Если результатом преобразования является prvalue, временная материализация применяется к нему, при этом тип prvalue считается скорректированным типом P .

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

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

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

Косвенная привязка

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

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

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

(до C++17)

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

(начиная с C++17)
const std::string& rs = "abc"; // rs ссылается на временный объект, копически инициализированный из символьного массива
const double& rcd2 = 2;        // rcd2 ссылается на временный объект со значением 2.0
int i3 = 2;
double&& rrd3 = i3;            // rrd3 ссылается на временный объект со значением 2.0

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

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

  • выражение prvalue объектного типа,
(до C++17)
  • преобразование материализации временного объекта,
(начиная с C++17)
  • выражение в скобках ( e ) , где e является одним из этих выражений,
  • встроенное выражение индексации (built-in subscript expression) вида a [ n ] или n [ a ] , где a является массивом и одним из этих выражений,
  • выражение доступа к члену класса (class member access expression) вида e. m , где e является одним из этих выражений, а m обозначает нестатический член данных объектного типа,
  • операция доступа по указателю на член (pointer-to-member operation) вида e. * mp , где e является одним из этих выражений, а mp является указателем на член данных,
  • преобразование const_cast , static_cast , dynamic_cast или reinterpret_cast без пользовательского преобразования, которое преобразует одно из этих выражений в glvalue, ссылающееся на объект, обозначенный операндом, или на его полный объект или подобъект ( явное приведение типа интерпретируется как последовательность таких преобразований),
  • условное выражение (conditional expression) вида cond ? e1 : e2 , являющееся glvalue, где e1 или e2 является одним из этих выражений, или
  • встроенное выражение с запятой (built-in comma expression) вида x, e , являющееся glvalue, где e является одним из этих выражений.

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

  • временный объект, привязанный к возвращаемому значению функции в return выражении, не продлевает время жизни: он уничтожается немедленно в конце return-выражения. Такое return выражение всегда возвращает висячую ссылку.
(до C++26)
  • временный объект, привязанный к ссылочному параметру в вызове функции, существует до конца полного выражения, содержащего этот вызов функции: если функция возвращает ссылку, которая переживает полное выражение, она становится висячей ссылкой.
  • Временный объект, привязанный к ссылке в инициализаторе, используемом в new-expression, существует до конца полного выражения, содержащего это new-expression, а не так долго, как инициализированный объект. Если инициализированный объект переживает полное выражение, его ссылочный член становится висячей ссылкой.
(since C++11)
  • временный объект, привязанный к ссылке в ссылочном элементе агрегата, инициализированного с использованием прямой инициализации синтаксиса ( круглые скобки ) существует до конца полного выражения, содержащего инициализатор, в отличие от списочной инициализации синтаксиса { фигурные скобки } .
struct A
{
    int&& r;
};
A a1{7}; // OK, lifetime is extended
A a2(7); // well-formed, but dangling reference
(since C++20)

В общем случае время жизни временного объекта не может быть дополнительно продлено путем «передачи его дальше»: вторая ссылка, инициализированная из переменной-ссылки или элемента данных, к которой был привязан временный объект, не влияет на его время жизни.

Примечания

Ссылки объявляются без инициализаторов только в объявлении параметров функции, в объявлении возвращаемого типа функции, в объявлении члена класса и с extern спецификатором.

До разрешения CWG issue 1696 , временный объект разрешалось привязывать к ссылочному члену в initializer list конструктора, и он сохранялся только до выхода из конструктора, а не в течение всего времени существования объекта. Такая инициализация стала некорректной после CWG 1696 , хотя многие компиляторы всё ещё поддерживают её (заметным исключением является clang).

Пример

#include <sstream>
#include <utility>
struct S
{
    int mi;
    const std::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;      // ошибка: нет инициализатора
extern int& ext_r; // OK
int main()
{
//  Lvalue
    int n = 1;
    int& r1 = n;                    // lvalue ссылка на объект n
    const int& cr(n);               // ссылка может быть более cv-квалифицированной
    volatile int& 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; // ссылка на результат преобразования
//  Rvalues
//  int& bad = 1;        // ошибка: нельзя привязать lvalue ссылку к rvalue
    const int& cref = 1; // привязана к rvalue
    int&& rref = 1;      // привязана к rvalue
    const A& cref2 = bar(); // ссылка на A подобъект временного объекта B
    A&& rref2 = bar();      // аналогично
    int&& xref = static_cast<int&&>(n); // прямая привязка к n
//  int&& copy_ref = n;                 // ошибка: нельзя привязать к lvalue
    double&& copy_ref = n;              // привязка к временному rvalue со значением 1.0
//  Ограничения времени жизни временных объектов
//  std::ostream& buf_ref = std::ostringstream() << 'a';
                     // временный ostringstream был привязан к левому операнду
                     // operator<<, но его время жизни закончилось на точке с запятой,
                     // поэтому buf_ref является висячей ссылкой
    S a {1, {2, 3}}; // временный pair {2, 3} привязан к ссылочному члену
                     // a.mp, и его время жизни продлено до соответствия
                     // времени жизни объекта a
    S* p = new S{1, {2, 3}}; // временный pair {2, 3} привязан к ссылочному
                             // члену p->mp, но его время жизни закончилось на точке с запятой
                             // p->mp является висячей ссылкой
    delete p;
    // Имитация [[maybe_unused]] примененного к следующим переменным:
    [](...){}
    (
        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 преобразование из 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 ссылочно-связанные типы были разрешены для косвенной привязки запрещено

Смотрите также