Перекомпилировав один проект в CLion — компилятором clang++ 3.8 вместо g++ 5.4,
set(CMAKE_CXX_COMPILER /usr/bin/clang++)

получил предупреждение компилятора:
warning: moving a temporary object prevents copy elision [-Wpessimizing-move]

(N)RVO шагнуло весьма далеко.

стало интересно разобраться
Итак создаём пример
Какой-то, класс — rvo,
Функция которая создаёт вектор объектов этого класса и возвращает его по значению.
Переменная, которая принимает этот вектор. Т.к. вектор возвращается по значению, чтобы избежать создания нового вектора конструктором копирования, применяем приведение std::move к возвращаемому значению функции, приводя его к rvalue и вызову конструктора перемещения.
#include <utility>
#include <iostream>
#include <vector>

class rvo
{
public:
	rvo(const std::string &s="Hello"):str(std::move(s)){	};
	void sayHello(){ std::cout<< str <<std::endl;
	}
private:
	std::string str;
};


auto getRvo(){
    std::vector<rvo> vec;
    for(auto a = 0; a< 10; ++a){
     	 rvo obj;
 	 vec.push_back(obj);
 }
 return vec;
}


int main(int argc, char const *argv[])
{
       auto j = std::move(getRvo());
	for (auto &a:j){
		a.sayHello();
	}
	return 0;
}


Компилируем
clang++ rvo.cpp -o rvo -std=c++14 -Wpessimizing-move


И получаем предупреждение
rvo.cpp:38:11: warning: moving a temporary object prevents copy elision [-Wpessimizing-move]
auto j = std::move(getRvo());
^
rvo.cpp:38:11: note: remove std::move call here
auto j = std::move(getRvo());



Смотрим стандарт 12.8.31 — вот нужные нам пункты.

— in a return statement in a function with a class return type, when the expression is the name of a
non-volatile automatic object (other than a function or catch-clause parameter) with the same cv-
unqualified type as the function return type, the copy/move operation can be omitted by constructing
the automatic object directly into the function’s return value

— when a temporary class object that has not been bound to a reference (12.2) would be copied/moved
to a class object with the same cv-unqualified type, the copy/move operation can be omitted by
constructing the temporary object directly into the target of the omitted copy/move


Т.ч. если типы создаваемой и возвращаяемой переменной совпадают (помним про Almost Always Auto), тогда применение std::move к объекту возвращаемому из функции по значению, для инициализации нового объекта — излишне. Правильная функция main в примере:

int main(int argc, char const *argv[])
{
       auto j = getRvo();
	for (auto &a:j){
		a.sayHello();
	}
	return 0;
}

Поделиться с друзьями
-->

Комментарии (5)


  1. Jigglypuff
    27.01.2017 14:33
    +1

    Майерс, Effective Modern C++, Item 24.

    In other words, they figure that given a function returning a local variable by value,
    such as this,
    Widget makeWidget() // "Copying" version of makeWidget
    {
     Widget w; // local variable
     … // configure w
     return w; // "copy" w into return value
    }
    

    they can “optimize” it by turning the “copy” into a move:
    Widget makeWidget() // Moving version of makeWidget
    {
     Widget w;
     …
     return std::move(w); // move w into return value
    } // (don't do this!)
    

    My liberal use of quotation marks should tip you off that this line of reasoning is
    flawed. But why is it flawed?
    It’s flawed, because the Standardization Committee is way ahead of such program?
    mers when it comes to this kind of optimization.

    Читайте классическую литературу, не придется наступать на грабли :)


    1. sborisov
      27.01.2017 14:45

      Про то, что говорит Майерс, мы все давно знаем. Тут же было применение std::move к уже возвращённому значению. И таких грабель как оказалось довольно много. Вот пример обсуждения:
      https://groups.google.com/forum/#!topic/fast-downward/fFSTDLRBp-A


      1. Jigglypuff
        27.01.2017 14:47

        Ответил Вам чуть ниже.


  1. sborisov
    27.01.2017 14:43

    ВЫ привели не тот пример.


    1. Jigglypuff
      27.01.2017 14:44
      +1

      Тот же самый, просто в данном примере std::move помещен внутри функции, а у Вас — снаружи.
      Суть от этого не меняется, и там, и тут — попытка предотвратить копирование, которое компиляторы могут предотвратить и сами.