#include <string>
#include <vector>
using namespace std;
auto f()
{
vector<string> coll{ "hello" };
//
// Must I use move(coll[0]) ?
//
return coll[0];
}
int main()
{
auto s = f();
DoSomething(s);
}
I know: If I just return coll;
, then coll
is guaranteed to be moved on return.
However, I am not sure: Whether coll[0]
is also guaranteed to be moved on return?
Update:
#include <iostream>
struct A
{
A() { std::cout << "constructed\n"; }
A(const A&) { std::cout << "copy-constructed\n"; }
A(A&&) { std::cout << "move-constructed\n"; }
~A() { std::cout << "destructed\n"; }
};
struct B
{
A a;
};
A f()
{
B b;
return b.a;
}
int main()
{
f();
}
gcc 6.2 and clang 3.8 outputs the same:
constructed
copy-constructed
destructed
destructed
When returning a local object, neither a copy nor a move will be used, but copy elision, which is to be preferred over moving. This is because the rules governing copy elision and moving of local objects are the same. When instead forcing a move by explicitly using std::move
as in
template<typename T>
std::string make_string(T const& x)
{
std::ostringstream str;
str << x
return std::move(str.str()); // not recommended
}
recent versions of clang issue a warning
moving a temporary object prevents copy elision [-Wpessimizing-move]
However, the situation in your code is different. Unlike std::ostringstream::str()
, which returns an object (a std::string
), std::vector<>::operator[]
, returns a reference, which must be converted to an object (since auto
removes references). In this case, copy elision is not possible (because the actual object is part of another object with non-trivial destructor) and std::move()
should be used to avoid a copy.
These considerations suggest to use std::move()
if unsure, but remove it if clang issues above warning.
The cleanest formulation of the "implicit move" rule is in [class.copy.elision]/3 of the current working paper:
In the following copy-initialization contexts, a move operation might be used instead of a copy operation:
If the expression in a return statement ([stmt.return]) is a (possibly parenthesized) id-expression that names an object with automatic storage duration declared in the body or parameter-declaration-clause of the innermost enclosing function or lambda-expression, or
[...]
overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue. If the first overload resolution fails or was not performed, or if the type of the first parameter of the selected constructor is not an rvalue reference to the object's type (possibly cv-qualified), overload resolution is performed again, considering the object as an lvalue.
Neither b.a
nor coll[0]
is an id-expression. Therefore, there is no implicit move. If you want a move, you'll have to do it explicitly.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With