I want to return a large object from a function, for example, the variable std::string s;
.
Do I need to write return std::move(s);
?
2 answers
If the type of the variable coincides with the type of the return value, then do not write T t; return std::move(t);
T t; return std::move(t);
.
It is necessary to write T f() { T t; return t; }
T f() { T t; return t; }
T f() { T t; return t; }
because in return
variable t
is considered rvalue.
The standard C ++ 11 says:
12.8 Copying and moving class objects [class.copy] p32
When the criteria for skipping copying / moving ( copy elision , including RVO) are met (except for restrictions on the parameters of the function), and the expression denoting the object to be copied is lvalue, then the constructor is selected as if it were rvalue.
One of the cases of copy skipping is NRVO :
In
return
, if the function returns a class *, and thereturn
expression is the name of a non-volatile
object with an automatic storage duration (except for the function parameter orcatch
), and its type is the same as the return type.
(* note: struct
and union
are also types-classes )
In C ++ 14 , the wording is slightly different, the case of return
considered separately from the copy skipping (RVO):
When the criteria for skipping copying / moving is met [...] and the expression in
return
is an lvalue orid-expression
(including in brackets) that denotes an object with automatic storage duration declared in the body or parameters of a function or lambda expression .
Thus, starting from C ++ 11, the standard guarantees that if the optimizer is turned off, and NRVO is not, then in the following cases the displacement constructor will be called, not copied:
struct T { T(); T(T&&); T(const T&); }; T f() { T t; return t; // Имя локальной переменной, вызов T::T(T&&) } T f() { T t; return (t); // Имя локальной переменной в скобках, вызов T::T(T&&) } T g(T t) { return t; // Имя параметра, вызов T::T(T&&) }
Since all that std::move(x)
does is to convert an expression into a reference to an rvalue, there is no point in writing T f() { T t; return move(t); }
T f() { T t; return move(t); }
T f() { T t; return move(t); }
. (This also takes away the possibility of NRVO).
Moreover, if the expression in return
not a name, then the automatic conversion to rvalue will not work.
T f1() { struct X { T t; } x; return xt; // Часть объекта, будет вызов T::T(const T&) // Надо написать std::move(xt) } T g1(T* pt) { return *pt; // Не имя локальной переменной или параметра, // Надо написать std::move(*pt) }
More complicated example:
T h1() { T a; while (...) { T b; if (...) { return b; // вызов T::T(T&&) } } return a; // вызов T::T(T&&) } T h2(T a, T b, bool c) { return c ? a : b; // Не имя локальной переменной или параметра, // надо написать std::move(с ? a : b) }
If the variable type differs from the return type , then the rules above do not work, and you should write std::move(t)
:
struct Base { virtual ~Base() {} }; struct Derived : Base {}; std::unique_ptr<Base> f() { std::unique_ptr<Derived> r; return std::move(r); // Нужен явный move, потому что типы разные. }
- By the way, the hose analyzer on such things swears. - Monah Tuk
- Please explain in more detail what is meant by "is not a name." - Cerbo
- About
return (t)
it would be necessary an explanation. Because An elementary check shows a call to a move constructor, not a copy. - αλεχολυτ - The phrase
конструктор "перемещения" - это тоже конструктор копирования
does not correspond to reality. A constructor cannot be both copying and moving. Accepts&
means this is the copy constructor, if&&
, then this is already a motion constructor. - αλεχολυτ - @alexolut, thanks, corrected. The standard really distinguishes between copy and move constructors. - Abyx
Meyers in "Modern and Effective C ++" gives advice (Section 5.3, item 26 in the original) NOT to apply std::move()
to local objects that can be optimized for the return value .
Roughly speaking, if it is the only object returned (that is, returned on all paths of execution), then it is not worth it. But if not - there may be different options.
- Yes, it can be, but it may not be! What if NRVO doesn't work? Here is the MSDN page: msdn.microsoft.com/en-us/library/ms364057 ( v=vs.80 ).aspx says that there will be no NRVO if:
Multiple return paths (even if the same named object is returned on all paths) with EH states introduced
- sys_dev - @sys_dev For a long time to paint everything, there is not a single page of reasoning and consideration of different situations. I just gave a brief summary. I don’t know how it is in Russian, but the original is definitely on the net, so take a look .. - Harry
- And who prevents you from doing the same as @Abyx? Create a question and answer it? And here is a link to this question - sys_dev
- @sys_dev Frankly speaking, I am not so long on this resource, so while I look closely :) I didn’t even notice that the author of the question and answer is the same - this has never occurred to me :) But if you are talking about a big detailed answer - then, frankly, a lot and a long time to write - you need to have time and an appropriate mood, now neither one nor the other is enough ... - Harry
- oneSimilar questions and answers you form a Russian-speaking programmer society! Someone from this society may well work side by side with you in the future. In other words, “you reap what you sow” - sys_dev
std::move(x)
? - VladDreturn
but iny = move(x)
orf(move(x))
. - Abyx