|
|||||||
Rvalue-ссылки в языке C++
Время создания: 14.06.2023 10:33
Автор: Дмитрий Пономарев
Текстовые метки: C++, Си++, ссылка, rvalue, lvalue, &&, двойной, амперсанд
Раздел: Компьютер - Программирование - Язык C++ (Си++) - Стандарт C++11 и выше
Запись: xintrea/mytetra_syncro/master/base/1686728621we8e7xdih3/text.html на raw.github.com
|
|||||||
|
|||||||
Rvalue-ссылки - это разновидность обычных C++ ссылок, отличие состоит в правилах инициализации и правилах разрешения перегрузок функций, имеющих параметры типа rvalue-ссылка. Тип rvalue-ссылки для типа T обозначаются через T&&. Для примеров будем использовать класс: class Int { int m_Value; public: Int(int val) : m_Value(val) {} int Get() const { return m_Value; } void Set(int val) { m_Value = val; } }; Как и обычные ссылки, rvalue-ссылки необходимо инициализировать. Int&& r0; // error C2530: 'r0' : references must be initialized Первым отличием rvalue-ссылок от обычных С++ ссылок заключается в том, что их нельзя инициализировать с помощью lvalue. Пример: Int i(7); Int&& r1 = i; // error C2440: 'initializing' : cannot convert from 'Int' to 'Int &&' Для корректной инициализации необходимо использовать rvalue: Int&& r2 = Int(42); // OK Int&& r3 = 5; // OK или lvalue должно быть явно приведено к типу rvalue-ссылки: Int&& r4 = static_cast<Int&&>(i); // OK Вместо оператора приведения к типу rvalue-ссылки обычно используется функция (точнее шаблон функции) std::move(), делающая то же самое (заголовочный файл <utility>). Rvalue ссылки можно инициализировать с помощью rvalue встроенного типа, для обычных ссылок это запрещено. int&& r5 = 2 * 2; // OK int& r6 = 2 * 2; // error После инициализации rvalue-ссылки можно использовать как обычные ссылки. Int&& r = 7; std::cout << r.Get() << '\n'; // Вывод: 7 r.Set(19); std::cout << r.Get() << '\n'; // Вывод: 19 Rvalue-ссылки неявно приводятся к обычным ссылкам. Int&& r = 5; Int& x = r; // OK const Int& cx = r; // OK Rvalue-ссылки редко используются как самостоятельные переменные, обычно они используются как параметры функций. В соответствии с правилами инициализации, если функция имеет параметры типа rvalue-ссылок, то ее можно вызвать только для rvalue аргументов. void Foo(Int&&); Int i(7); Foo(i); // error, lvalue аргумент Foo(std::move(i)); // OK Foo(Int(4)); // OK Foo(5); // OK Если имеются несколько перегруженных функций, то при разрешении перегрузки для rvalue аргумента версия с параметром типа rvalue-ссылка имеет приоритет над версией с параметром типа обычная ссылка или обычная ссылка на константу, хотя последние и могут быть допустимыми вариантами. И это правило является второй ключевой особенностью rvalue-ссылок. Функция с параметром, передаваемым по значению, и перегруженная версия, имеющая параметр типа rvalue-ссылка, будут неразрешимы (ambiguous) для rvalue аргументов. Для примера рассмотрим перегруженные функции void Foo(Int&&); void Foo(const Int&); и несколько вариантов их вызова Int i(7); Foo(i); // Foo(const Int&) Foo(std::move(i)); // Foo(Int&&) Foo(Int(6)); // Foo(Int&&) Foo(9); // Foo(Int&&) Следует обратить внимание на один важный момент: именованная rvalue-ссылка сама по себе является lvalue. Int&& r = 7; Foo(r); // Foo(const Int&) Foo(std::move(r)); // Foo(Int&&) Это надо учитывать при определении функций, имеющих параметры типа rvalue-ссылка, такие параметры являются lvalue и могут потребовать использования std::move(). См. пример перемещающего конструктора и оператора перемещающего присваивания в разделе 2.4. Еще одно нововведение С++11, связанное с rvalue-ссылками — это ссылочные квалификаторы для нестатических функций-членов. Они позволяют перегружать по типу (lvalue/rvalue) скрытого параметра this. class X { public: X(); void DoIt() &; // this указывает на lvalue void DoIt() &&; // this указывает на rvalue // ... }; X x; x.DoIt(); // DoIt() & X().DoIt(); // DoIt() && |
|||||||
Так же в этом разделе:
|
|||||||
|
|||||||
|