Reputation: 127
I'm trying to learn move semantics well enough to introduce it to my students. I've been using highly simplified vector- or string-like classes that manage memory and whose members output messages to demonstrate their activity. I'm trying to develop a simple set of examples to show students.
Construction elision for RVO and elsewhere in gcc 4.7 and clang aggressively eliminates copy and move construction, so while I can easily see move assignment at work, the only time I've seen move construction at work is if I turn off construction elision in gcc 4.7 with -fno-elide-constructors.
An explicit copy construction statement
MyString newString(oldString);
will invoke the copy constructor even if elision is enabled. But something like
MyString newString(oldString1 + oldString2);
doesn't invoke the move constructor because of the elision.
Anything explicitly using std::move won't make a simple example because explaining std::move has to come later.
So my question: Is there a simple code example that will invoke move construction even if copy/move constructors are being elided?
Upvotes: 7
Views: 568
Reputation: 208323
The simple example would be an argument to a function that is returned. The standard explicitly forbids eliding the move in this case (not that they could...):
std::vector<int> multiply( std::vector<int> input, int value ) {
for (auto& i : input )
i *= value;
return input;
}
Additionally, you can explicitly request move construction for an even simpler although a bit more artificial example:
T a;
T b( std::move(a) );
Uhm... yet another that does not involve std::move
(it can technically be elided, but most compilers will probably not):
std::vector<int> create( bool large ) {
std::vector<int> v1 = f();
std::vector<int> v2 = v1; // modify both v1 and v2 somehow
v2.resize( v2.size()/2 );
if ( large ) {
return v1;
} else {
return v2;
}
}
While the optimizer can elide it by rewriting the code as:
std::vector<int> create( bool large ) {
if ( large ) {
std::vector<int> v1 = f();
std::vector<int> v2 = v1; // modify both v1 and v2 somehow
v2.resize( v2.size()/2 );
return v1;
} else {
std::vector<int> v1 = f();
std::vector<int> v2 = v1; // modify both v1 and v2 somehow
v2.resize( v2.size()/2 );
return v2;
}
}
I pretty much doubt that the compiler will actually do it. Note that in each code path the object being returned in known before v1
and v2
are created, so the optimizer can locate the proper object in the return location after the rewrite.
The situations where copy/move can be elided are described in 12.8/31. If you manage to write code that does not fall into those categories and the type has a move constructor, the move constructor will be called.
Upvotes: 7
Reputation: 476970
Hmmm, let's see:
MyString newString(oldString)
is a copy. There's nothing to elide here; we really end up with two objects.
MyString newString(oldString1 + oldString2);
copies from a temporary, so the copy can be elided and the concatenation is constructed directly in-place.
Here's a really terribly cheap example of un-elidable move construction:
MyString boo()
{
MyString s("Hello");
return std::move(s); // move-construction from the local "s", never elided
}
The explicit cast makes s
ineligible for RVO, so the return value will be move-constructed from s
.
Upvotes: 1