Ответ 1
Основная задача интерфейса компилятора - удалить имена из всего, чтобы разрешить основные семантические структуры.
Стремясь избежать имен, помогает избежать ненужного обращения к адресам объектов, что может неинтуитивно остановить компилятор от манипулирования данными. Но есть достаточно способов получить адрес временного, что все это, но спорный. И названные объекты особенны в том, что они не имеют права на конструкторский эликсир в С++, но, как вы говорите, перемещение семантики устраняет наиболее дорогостоящую ненужную копию-конструкцию.
Просто сосредоточьтесь на написании читаемого кода.
Ваш первый пример устраняет копию n
, но в С++ 11 вместо этого вы можете использовать семантику перемещения: DoSomething( std::move( n ) )
.
В примере s1 + s2 + s3
также верно, что С++ 11 делает вещи более эффективными, но перемещение семантики и устранение временных ситуаций - это разные вещи. Конструктор перемещения просто делает строительство временного менее дорогостоящим.
Я тоже был под недоразумением, что С++ 11 устранит временные файлы, если вы использовали идиому
// What you should use in C++03
foo operator + ( foo lhs, foo const & rhs ) { return lhs += rhs; }
Это фактически неверно; lhs
- это именованный объект, а не временный, и он не подходит для формы оптимизации возвращаемого значения. На самом деле, в С++ 11 это создаст копию, а не движение! Вам нужно исправить это с помощью std::move( lhs += rhs );
.
// What you should use in C++11
foo operator + ( foo lhs, foo const & rhs ) { return std::move( lhs += rhs ); }
В вашем примере используется std::string
, а не foo
, и что operator+
определяется (по существу, и с С++ 03) как
// What the C++03 Standard Library uses
string operator + ( string const & lhs, string const & rhs )
{ return string( lhs ) += rhs; } // Returns rvalue expression, as if moved.
Эта стратегия имеет сходные свойства с вышеизложенным, поскольку временное дисквалифицировано для копирования, когда оно привязано к ссылке. Есть два возможных исправления, которые дают выбор между скоростью и безопасностью. Ни одно исправление не совместимо с первой идиомой, которая с move
уже реализует безопасный стиль (и как таковой вы должны использовать!).
Безопасный стиль.
Здесь нет именованных объектов, но временная привязка к аргументу lhs
не может быть напрямую сконструирована в привязке результата к эталонному прекращению копирования.
// What the C++11 Standard Library uses (in addition to the C++03 library style)
foo operator + ( foo && lhs, foo const & rhs )
{ return std::move( lhs += rhs ); }
Небезопасный стиль.
Вторая перегрузка, принимающая ссылку rvalue и возвращающая ту же ссылку, полностью исключает промежуточное временное (не полагается на elision), позволяя цепочке вызовов +
идеально преобразовываться в вызовы +=
. Но, к сожалению, он также дисквалифицирует оставшиеся временные в начале цепочки вызовов из продления жизни, привязывая его к ссылке. Таким образом, возвращаемая ссылка действительна до точки с запятой, но затем она уходит, и ничто не может ее остановить. Таким образом, это в основном полезно внутри чего-то вроде библиотеки выражений шаблонов с документированными ограничениями на то, какие результаты могут быть привязаны к локальной ссылке.
// No temporary, but don't bind this result to a local!
foo && operator + ( foo && lhs, foo const & rhs )
{ return std::move( lhs += rhs ); }
Оценка документации библиотеки как таковой требует небольшой оценки навыков авторов библиотеки. Если они говорят, что делают что-то необычно, потому что это всегда более эффективно, будьте скептически настроены, потому что С++ не предназначен специально для того, чтобы быть изворотливым, но он призван быть эффективным.
Однако в случае шаблонов выражения, где временные числа включают в себя сложные вычисления типа, которые будут прерваны присвоением именованной переменной конкретного типа, вы должны абсолютно слушать то, что говорят авторы. В таком случае они были бы, вероятно, гораздо более осведомленными.