RVO, move semantics and the struggle towards optimal code - c++

If I get it correctly, move semantics allows to move and reuse resources from temporary, unnamed objects. RVO, albeit preceding move semantics goes further and "steals" the entire object to avoid the extra constructor call and assignment/copy function.
This seems a bit counter intuitive to me, wouldn't it be that much faster, simple and user obvious if the called constructor uses directly the address of the final lvalue target to directly emplace data where the user needs it?
I mean, "create this object in this location" seems a bit more intuitive than "create this object somewhere, then copy it to its right location".

Yes it is "a bit counter intuitive". With copy elision enabled all side effects of the constructor are elided, too.
#include <iostream>
struct X {
X() { std::cout << "Construct" << std::endl; }
X(X&&) { std::cout << "Move" << std::endl; }
~X() { std::cout << "Destruct" << std::endl; };
};
X f() { return X(); }
int main()
{
X x(f());
return 0;
}
Copy elision: g++ -std=c++11 src-test/main.cc
Construct
Destruct
No copy elision: g++ -std=c++11 -fno-elide-constructors src-test/main.cc
Construct
Move
Destruct
Move
Destruct
Destruct
The compiler, knowing the hardware the program/library is build for, is able to apply (optional) copy elision.
The C++ language, itself, is not aware of hardware specific return mechanisms. Hence it is not possible to construct at a certain address in this context.

Related

Why move constructor is called instead of copy constructor

I'm trying to understand a move constructor,
usually a copy constructor is called when objects are copied
that what happen when I do not provide a move constructor,
but when I add a move constructor it is called instead of my copy constructor,
here is my code:
#include <iostream>
#include <vector>
using namespace std;
struct A
{
A()
{
cout << "A's constructor" << endl;
}
A(const A& rhs)
{
cout << "A's copy constructor" << endl;
}
A(A&& rhs)
{
cout << "A's move constructor" << endl;
}
};
int main() {
vector<A> v;
cout << "==> push_back A():";
v.push_back(A());
cout << "==> push_back A():" << endl;
v.push_back(A());
return 0;
}
does the compiler try to optimize my code and choose the better method ?
Basically, yes.
However, this is not about compiler optimisations as much as it is about how the language itself has been optimised.
Half of the point of move semantics is to allow efficient use of temporaries. That's why temporaries bind to rvalue refs and it's how the entire move semantics thing works. When you don't have a temporary but you wish to move something anyway, you write std::move to obtain an rvalue and trigger this same behaviour.
There is no point copying those temporaries when they are about to be destroyed anyway.
In concert with copy/move elision, this feature will result in much less redundant computing.
Note, however, that your move constructor is not terribly useful — if you ever wanted to make it actually do something (like, um, move stuff) you'd have to remove that const.
rvalue references are better candidates to bind temporaries than const lvalue references. It is not about optimizing (as in compiler optimizations), it is about following standard. The actual optimization would be to not call copy constructor at all.

Is pass by value that much faster?

I've heard that you should always prefer "pass by value" in C++11 because of the introduction of move semantics. I wanted to see what the hype was all about and constructed a test case. First my class:
struct MyClass {
MyClass() { }
MyClass(const MyClass&) { std::cout << "Copy construct" << std::endl; }
MyClass(MyClass&&) { std::cout << "Move construct" << std::endl; }
~MyClass() { }
};
And the test harness:
class Test
{
public:
void pass_by_lvalue_ref(const MyClass& myClass)
{
_MyClass.push_back(myClass);
}
void pass_by_rvalue_ref(MyClass&& myClass)
{
_MyClass.push_back(std::move(myClass));
}
void pass_by_value(MyClass myClass)
{
_MyClass.push_back(std::move(myClass));
}
private:
std::vector<MyClass> _MyClass;
};
Presumably, pass_by_value should outperform pass_by_lvalue_ref and pass_by_rvalue_ref (together, not separately).
int main()
{
MyClass myClass;
Test Test;
std::cout << "--lvalue_ref--\n";
Test.pass_by_lvalue_ref(myClass);
std::cout << "--rvalue_ref--\n";
Test.pass_by_rvalue_ref(MyClass{});
std::cout << "--value - lvalue--\n";
Test.pass_by_value(myClass);
std::cout << "--value - rvalue--\n";
Test.pass_by_value(MyClass{});
}
This is my output on GCC 4.9.2 with -O2:
--lvalue_ref--
Copy construct
--rvalue_ref--
Move construct
Copy construct
--value - lvalue--
Copy construct
Move construct
Copy construct
Copy construct
--value - rvalue--
Move construct
As you can see, the non-pass_by_value functions requires a total of 2 copy constructs and 1 move construct. The pass_by_value function requires a total of 3 copy constructs and 2 move constructs. It looks like that, as expected, the object is going to be copied anyway, so why does everyone say pass by value?
First, your reporting is entirely flawed. Each of your functions pushes back to the same vector. When that vector runs out of capacity (which depends upon how many items you've inserted so far), it is going to trigger a re-allocation which will require more moves and/or copies than an insertion which doesn't trigger an allocation.
Second, std::vector::push_back has a strong exception safety guarantee. So if your move constructor is not noexcept, it will not use it (unless the class is non-copyable). It will use the copy constructor instead.
Third,
I've heard that you should always prefer "pass by value" in C++11
because of the introduction of move semantics.
I'm pretty sure you didn't hear that from any reputable source. Or are actually inappropriately paraphrasing what was actually said. But I don't have the source of the quote. What is usually advised is actually that if you are going to copy your arguments in your function anyway, don't. Just do it in the parameter list (via pass by value). This will allow your function to move r-value arguments straight to their destination. When you pass l-values, they will be copied, but you were going to do that anyway.
If you are going to make an internal copy, then passing by value will do exactly one move construct more than the pair of overloads (pass by rvalue ref)+(pass by const lvalue ref).
If move construct is cheap, this is a small amount of runtime overhead in exchange for less compile time and code maintenance overhead.
The idiom is "Want speed? Making a copy anyhow? Pass by value, instead of by const lvalue reference." in reality.
Finally, your benchmark is flawed as you failed to reserve(enough) before your push backs. Reallocation can cause extra operations. Oh, and make your move constructor noexcept, as conforming libraries will prefer a copy to a move if move can throw in many situations.

C++11 Move constructor optimization

I'm currently trying to get a hang of move constructor.
I came upon the following (Compiled using g++ d.cpp --std=c++11 -O3)
class A {
string _x;
public:
A(string x) { cout << "default contrsutctor: " << x << "\n"; _x = x; }
A(const A& other) { cout << "copy contrsutctor: " << other._x << "\n"; _x = other._x; }
A(A&& other) { cout << "move contrsutctor: " << other._x << "\n"; _x = other._x; }
A foo() {
cout << "foo: " << _x << "\n";
return A("foo");
}
};
int main()
{
A a;
A b = a;
b.foo();
}
I expect this to output:
default contrsutctor: a
move contrsutctor: a
foo: a
default contrsutctor: foo
However the output is:
default contrsutctor: a
copy contrsutctor: a
foo: a
default contrsutctor: foo
Why isn't the A b = a line optimized to use the move constructor? The a object is never used afterwards, so it would be safe to optimize the code to use it instead of the copy constructor.
I know I could force the move contructor to be invoked with std::move(), but I'd prefer this to happen automatically in cases like this one.
Why isn't the A b = a line optimized to use the move constructor?
What you can do in copy constructor and move constructor could be totally different. The compiler cannot guarantee that the results of the two constructors are identical. Implementing this kind of optimization has the potential of changing the behavior of your program, which breaks the as-if rule.
You need to use std::move to cast a to A&&:
#include <utility>
int main()
{
A a("a");
A b = std::move(a);
b.foo();
}
A correct implementation of the move constructor should be:
A(A&& other)
: _x(std::move(other._x))
{}
After the line A b = std::move(a);, a should be "empty". In this case, a._x will be empty. as pointed by #TonyD in the comments, a._str could be in an unspecified but valid state (move constructor of std:string). You should use a with caution after this line.
A b = a; always invokes the copy constructor, no matter if it could invoke the move constructor. Additionally the lifetime of the object a continues after the assignment, even it is not used anymore.
If you want to use the move constructor, you have to make it explicit:
A b = std::move(a);
Note that this can be dangerous, as a is still accessible after the move. If you accidentally use it later, there may be undefined behavior.
Think about why it should happen automatically. In the example you gave, there is no need, as you can as well use a instead of b. In many cases where it would make more sense move constructor/assignment would be used automatically, e.g. A a; a = foo();.
Why isn't the A b = a line optimized to use the move constructor?
Because that would change the observable behavior of the program. The compiler is not permitted to freely change the observable behavior of the program (§1.9/1), except under very specific circumstances (§12.8/31). This is not one of those circumstances. Remove the side effects from your constructors, and the compiler may optimize them away. Of course, if you remove the side effects, then you won't notice if the compiler optimizes the constructor calls away (unless you examine the assembly or binary output), but that's the whole point.

std::vector and move constructor not working with clang and libc++ [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How to enforce move semantics when a vector grows?
insert, push_back and emplace(_back) can cause a reallocation of a std::vector. I was baffled to see that the following code copies the elements instead of moving them while reallocating the container.
#include <iostream>
#include <vector>
struct foo {
int value;
explicit foo(int value) : value(value) {
std::cout << "foo(" << value << ")\n";
}
foo(foo const& other) noexcept : value(other.value) {
std::cout << "foo(foo(" << value << "))\n";
}
foo(foo&& other) noexcept : value(std::move(other.value)) {
other.value = -1;
std::cout << "foo(move(foo(" << value << "))\n";
}
~foo() {
if (value != -1)
std::cout << "~foo(" << value << ")\n";
}
};
int main() {
std::vector<foo> foos;
foos.emplace_back(1);
foos.emplace_back(2);
}
On my specific machine using my specific compiler (GCC 4.7) this prints the following:
foo(1)
foo(2)
foo(foo(1))
~foo(1)
~foo(1)
~foo(2)
However, when deleting the copy constructor (foo(foo const&) = delete;), the following (expected) output is generated:
foo(1)
foo(2)
foo(move(foo(1))
~foo(1)
~foo(2)
Why is that? Would’t moving generally be more efficient, or at least not much less efficient, than copying?
It bears noting that GCC 4.5.1 does the expected thing – is this a regression in GCC 4.7 or is it some deviously clever optimisation because the compiler sees that my object is cheap to copy (but how?!)?
Also note that I made sure that this is caused by reallocation, by experimentally putting a foos.reserve(2); in front of the insertions; this causes neither copy nor move to be executed.
The short answer is that I think #BenVoigt is basically correct.
In the description of reserve (§23.3.6.3/2), it says:
If an exception is thrown other than by the move constructor of a non-CopyInsertable type, there are no effects.
[And the description of resize in §23.3.6.3/12 requires the same.]
This means that if T is CopyInsertable, you get strong exception safety. To assure that, it can only use move construction if it deduces (by unspecified means) that move construction will never throw. There's no guarantee that either throw() or noexcept will be necessary or sufficient for that though. If T is CopyInsertable, it can simply choose to always use copy construction. Basically, what's happening is that the standard requires copy construction-like semantics; the compiler can only use move construction under the as-if rule, and it's free to define when or if it'll exercise that option.
If T is not CopyInsertable, reallocation will use move construction, but exception safety depends on whether T's move constructor can throw. If it doesn't throw, you get strong exception safety, but if it throws, you don't (I think you probably get the basic guarantee, but maybe not even that and definitely no more).
Tip-of-trunk clang + libc++ gets:
foo(1)
foo(2)
foo(move(foo(1))
~foo(2)
~foo(1)
If you remove the noexcept from the move constructor, then you get the copy solution:
foo(1)
foo(2)
foo(foo(1))
~foo(1)
~foo(2)
~foo(1)
It's not a regression, it's a bug fix. The standard specifies that std::vector will only prefer an element move constructor which is non-throwing.
See also this explanation and this bug report.
This question is also relevant.

compiler optimization

So I have a question for you. :)
Can you tell me the output the following code should produce?
#include <iostream>
struct Optimized
{
Optimized() { std::cout << "ctor" << std::endl; }
~Optimized() { std::cout << "dtor" << std::endl; }
Optimized(const Optimized& copy) { std::cout << "copy ctor" << std::endl; }
Optimized(Optimized&& move) { std::cout << "move ctor" << std::endl; }
const Optimized& operator=(const Optimized& rhs) { std::cout << "assignment operator" << std::endl; return *this; }
Optimized& operator=(Optimized&& lhs) { std::cout << "move assignment operator" << std::endl; return *this; }
};
Optimized TestFunction()
{
Optimized a;
Optimized b = a;
return b;
}
int main(int argc, char* argv[])
{
Optimized test = TestFunction();
return 0;
}
My first response would be:
ctor
copy ctor
move ctor
dtor
dtor
dtor
and it IS true, but only if compiler optimization is turned off. When optimization is turned ON then the output is entirely different. With optimization turned on, the output is:
ctor
copy ctor
dtor
dtor
With compiler optimization, the test variable is the return variable.
My question is, what conditions would cause this to not be optimized this way?
I have always been taught that returning a struct/class which results in extra copy constructors could better be optimized by being passed in as a reference but the compiler is doing that for me. So is return a structure still considered bad form?
This is known as Copy Elision and is a special handling instead of copying/moving.
The optimization is specifically allowed by the Standard, as long as it would be possible to copy/move (ie, the method is declared and accessible).
The implementation in a compiler is generally referred to, in this case, as Return Value Optimization. There are two variations:
RVO: when you return a temporary (return "aa" + someString;)
NRVO: N for Named, when you return an object that has a name
Both are implemented by major compilers, but the latter may kick in only at higher optimization levels as it is more difficult to detect.
Therefore, to answer your question about returning structs: I would recommend it. Consider:
// Bad
Foo foo;
bar(foo);
-- foo can be modified here
// Good
Foo const foo = bar();
The latter is not only clearer, it also allows const enforcement!
Both outputs are permissible. The C++03 language standard says, in clause 12.8/15:
When certain criteria are met, an implementation is allowed to omit the copy construction of a class object,
even if the copy constructor and/or destructor for the object have side effects. In such cases, the implementation
treats the source and target of the omitted copy operation as simply two different ways of referring to
the same object, and the destruction of that object occurs at the later of the times when the two objects
would have been destroyed without the optimization.111) This elision of copy operations is permitted in the
following circumstances (which may be combined to eliminate multiple copies):
in a return statement in a function with a class return type, when the expression is the name of a
non-volatile automatic object with the same cv-unqualified type as the function return type, the copy
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 to a class
object with the same cv-unqualified type, the copy operation can be omitted by constructing the temporary
object directly into the target of the omitted copy
The output this code will produce is unpredictable, since the language specification explicitly allows optional elimination (elision) of "unnecessary" temporary copies of class objects even if their copy constructors have side effects.
Whether this will happen or not might depend on may factors, including the compiler optimization settings.
In my opinion calling the above copy elision an "optimization" is not entirely correct (although the desire to use this term here is perfectly understandable and it is widely used for this purpose). I'd say that the term optimization should be reserved to situations when the compiler deviates from the behavior of the abstract C++ machine while preserving the observable behavior of the program. In other words, true optimization implies violation of the abstract requirements of the language specification. Since in this case there's no violation (the copy elision is explicitly allowed by the standard), there's no real "optimization". What we observe here is just how the C++ language works at its abstract level. No need to involve the concept of "optimization" at all.
Even when passing back by value the compiler can optimise the extra copy away using Return Value Optimisation see; http://en.wikipedia.org/wiki/Return_value_optimization