Возвращаешь из функции по значению? Не хочешь платить за копирование? Не переплачивай


Перекомпилировав один проект в 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;
}

-->


К сожалению, не доступен сервер mySQL