c ++ 11 Оптимизация возвращаемого значения или перемещение?

Я не понимаю, когда я должен использовать std::move и когда я должен позволить оптимизировать компилятор … например:

 using SerialBuffer = vector; // let compiler optimize it SerialBuffer read( size_t size ) const { SerialBuffer buffer( size ); read( begin( buffer ), end( buffer ) ); // Return Value Optimization return buffer; } // explicit move SerialBuffer read( size_t size ) const { SerialBuffer buffer( size ); read( begin( buffer ), end( buffer ) ); return move( buffer ); } 

Что я должен использовать?

    Используйте исключительно первый метод:

     Foo f() { Foo result; mangle(result); return result; } 

    Это уже позволит использовать конструктор перемещения, если он доступен. Фактически, локальная переменная может связываться с ссылкой rvalue в операторе return точно, когда разрешено копирование.

    Ваша вторая версия активно запрещает копирование. Первая версия универсально лучше.

    Все возвращаемые значения либо уже moved либо оптимизированы, поэтому нет необходимости явно перемещаться с возвращаемыми значениями.

    Составителям разрешено автоматически перемещать возвращаемое значение (чтобы оптимизировать копию) и даже оптимизировать ход!

    Раздел 12.8 стандартного проекта n3337 (C ++ 11):

    Когда определенные критерии выполняются, реализации разрешено опускать конструкцию копирования / перемещения объекта classа, даже если конструктор copy / move и / или деструктор для объекта имеют побочные эффекты. В таких случаях реализация рассматривает источник и цель пропущенной операции копирования / перемещения как просто два разных способа обращения к одному и тому же объекту, а уничтожение этого объекта происходит в более поздние времена, когда эти два объекта были бы уничтожается без оптимизации. Это разрешение операций копирования / перемещения, называемое копированием , разрешено в следующих случаях (которые могут быть объединены для устранения нескольких копий):

    […]

    Пример :

     class Thing { public: Thing(); ~Thing(); Thing(const Thing&); }; Thing f() { Thing t; return t; } Thing t2 = f(); 

    Здесь критерии для элиции можно объединить, чтобы устранить два вызова конструктора копирования classа Thing : копирование локального автоматического объекта t во временный объект для возвращаемого значения функции f() и копирование этого временного объекта в объект t2 . Фактически, построение локального объекта t можно рассматривать как непосредственную инициализацию глобального объекта t2 , и это разрушение объекта произойдет при выходе программы. Добавление конструктора перемещения в Thing имеет тот же эффект, но это конструкция перемещения от временного объекта до t2 который отклоняется. – конец примера ]

    Когда критерии для выполнения операции копирования выполняются или выполняются, за исключением того факта, что исходный объект является параметром функции, а подлежащий копированию объект обозначается значением lvalue, разрешение перегрузки для выбора конструктора для копии сначала выполнялось так, как если бы объект был обозначен rvalue. Если сбой разрешения перегрузки или тип первого параметра выбранного конструктора не является ссылкой rvalue на тип объекта (возможно, cv-qualified), разрешение перегрузки выполняется снова, считая объект как lvalue.

    Это довольно просто.

    return buffer;

    Если вы это сделаете, то произойдет либо NRVO, либо нет. Если этого не произойдет, buffer будет перемещен.

    return std::move( buffer );

    Если вы это сделаете, NVRO не произойдет, и buffer будет перенесен.

    Таким образом, нет ничего, чтобы выиграть, используя std::move here, и многое потерять.

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

     Buffer read(Buffer&& buffer) { //... return std::move( buffer ); } 

    Если buffer является ссылкой на rvalue, вы должны использовать std::move . Это связано с тем, что ссылки не подходят для NRVO, поэтому без std::move это приведет к копированию из lvalue.

    Это всего лишь пример правила «всегда move ссылки rvalue и forward универсальные ссылки», что имеет приоритет над правилом «никогда не move возвращаемое значение».

    Если вы возвращаете локальную переменную, не используйте move() . Это позволит компилятору использовать NRVO, и в противном случае компилятору все же будет разрешено выполнить перемещение (локальные переменные становятся значениями R в операторе return ). Использование move() в этом контексте просто блокирует NRVO и заставляет компилятор использовать перемещение (или копию, если перемещение недоступно). Если вы возвращаете что-то иное, чем локальная переменная, NRVO в любом случае не является вариантом, и вы должны использовать move() если (и только если) вы намерены похитить объект.

    Давайте будем гением компьютера.