Copy/move elision и универсальные ссылки
Copy/move elision представляет собой оптимизацию, когда компилятор может убрать определенные вызовы конструктора копирования и деструктора, но только при возврате объекта из функции и если тип возвращаемого объекта совпадает с типом функции.
В результате при возврате из функции применение std::move() способно понизить производительность, ограничив тем самым компилятор в Copy elision оптимизации, ведь отсутствие конструктора, по сути, быстрее, чем конструктор перемещения.
В данном случае
Универсальные ссылки
Вернувшись к теме одной из предыдущих статей, скажем, что универсальные ссылки способны быть как rvalue-, так и lvalue-ссылкой — это зависит от аргументов либо результата функции. Применяются они в шаблонах и в auto&&:
На основе данного шаблона компилятор генерирует две функции, причем одна из них принимает lvalue, а вторая — rvalue, если они будут применяться. Когда разработчик желает использовать перемещение для rvalue-ссылки и обычное простое копирование для lvalue-ссылки, он может применить
Собственно говоря, универсальная ссылка просто обязана быть шаблонным параметром, откуда в нашем примере и взялось странное определение шаблона функции в формате T&&. Таким образом,
Но вообще, особой разницы между универсальной и rvalue-ссылкой не существует. Можно сказать, что универсальная ссылка является лишь удобной абстракцией над rvalue-ссылкой, которой, как раз таки, многие и пользуются. Но как же тогда rvalue-ссылка превращается в lvalue-ссылку, имея lvalue-аргумент? Ответ — путем свертывания ссылок.
Когда происходит вызов
Таким образом, получается ссылка на ссылку! Сделать так вручную невозможно, однако шаблоны могут. Потом компилятор свертывает ссылку на ссылку, а происходит это по определенному правилу: результатом свертывания становится rvalue-ссылка, но лишь в том случае, если обе ссылки — это rvalue-ссылки. И, как раз из-за данного безобразия и проще применять абстракцию универсальных ссылок.