Copy/move elision и универсальные ссылки | OTUS

Copy/move elision и универсальные ссылки

Copy/move elision представляет собой оптимизацию, когда компилятор может убрать определенные вызовы конструктора копирования и деструктора, но только при возврате объекта из функции и если тип возвращаемого объекта совпадает с типом функции.

В результате при возврате из функции применение std::move() способно понизить производительность, ограничив тем самым компилятор в Copy elision оптимизации, ведь отсутствие конструктора, по сути, быстрее, чем конструктор перемещения.

Screenshot_1-1801-593795.png

В данном случае std::move() лишь замедляет код, добавляя излишний вызов String(String&& other) и ~String(). При этом стоит учесть, что в C++20 copy/move elision может быть расширен, в результате чего в ряде случаев применение std::move() тоже снизит производительность.

Универсальные ссылки

Вернувшись к теме одной из предыдущих статей, скажем, что универсальные ссылки способны быть как rvalue-, так и lvalue-ссылкой — это зависит от аргументов либо результата функции. Применяются они в шаблонах и в auto&&:

Screenshot_2-1801-5db497.png

На основе данного шаблона компилятор генерирует две функции, причем одна из них принимает lvalue, а вторая — rvalue, если они будут применяться. Когда разработчик желает использовать перемещение для rvalue-ссылки и обычное простое копирование для lvalue-ссылки, он может применить std::forward(), приводящий свой аргумент к rvalue лишь тогда, когда его тип — это rvalue-ссылка. Что касается std::forward(), то он требует явного указания шаблонного параметра.

Собственно говоря, универсальная ссылка просто обязана быть шаблонным параметром, откуда в нашем примере и взялось странное определение шаблона функции в формате T&&. Таким образом, std::vector<T>&& — это уже не универсальная ссылка, а rvalue-ссылка.

Но вообще, особой разницы между универсальной и rvalue-ссылкой не существует. Можно сказать, что универсальная ссылка является лишь удобной абстракцией над rvalue-ссылкой, которой, как раз таки, многие и пользуются. Но как же тогда rvalue-ссылка превращается в lvalue-ссылку, имея lvalue-аргумент? Ответ — путем свертывания ссылок.

Когда происходит вызов template_func(string), компилятор генерирует следующий заголовок функции:

Screenshot_3-1801-81838a.png

Таким образом, получается ссылка на ссылку! Сделать так вручную невозможно, однако шаблоны могут. Потом компилятор свертывает ссылку на ссылку, а происходит это по определенному правилу: результатом свертывания становится rvalue-ссылка, но лишь в том случае, если обе ссылки — это rvalue-ссылки. И, как раз из-за данного безобразия и проще применять абстракцию универсальных ссылок.

Источник

Не пропустите новые полезные статьи!

Спасибо за подписку!

Мы отправили вам письмо для подтверждения вашего email.
С уважением, OTUS!

Автор
0 комментариев
Для комментирования необходимо авторизоваться
Популярное
Сегодня тут пусто