Why copy elision not working with std::move? - c++

I use the code below to test copy elision:
class foo
{
public:
foo() {cout<<"ctor"<<endl;};
foo(const foo &rhs) {cout<<"copy ctor"<<endl;}
};
int g(foo a)
{
return 0;
}
int main()
{
foo a;
g(std::move(a));
return 0;
}
I expected only the default constructor would be called because the argument of g() is an rvalue and copy will be elided. But the result shows that both the default constructor and the copy constructor are called. Why?
And if I change the function call to g(foo()), the copy will be elided. What's the difference between the return types of foo() and std::move(a)? How can I make the compiler elide copy on an lvalue?

Copy elision for can only occur in a few specific situations, the most common of which is the copying of a temporary (the others are returning locals, and throwing/catching exceptions). There is no temporary being produced by your code, so no copy is elided.
The copy constructor is being called because foo does not have a move constructor (move constructors are not implicitly generated for classes with explicit copy constructors), and so std::move(a) matches the foo(const foo &rhs) constructor (which is used to construct the function argument).
A copy of an lvalue can be elided in the following situations (although there is no way to force a compiler to perform the elision):
foo fn() {
foo localAutomaticVariable;
return localAutomaticVariable; //Copy to construct return value may be elided
}
int main() {
try {
foo localVariable;
throw localVariable; //The copy to construct the exception may be elided
}
catch(...) {}
}
If you want to avoid copies when passing function arguments, you can use a move constructor which pilfers the resources of the objects given to it:
class bar {
public:
bar() {cout<<"ctor"<<endl;};
bar(const bar &rhs) {cout<<"copy ctor"<<endl;}
bar(bar &&rhs) {cout<<"move ctor"<<endl;}
};
void fn(bar a)
{
}
//Prints:
//"ctor"
//"move ctor"
int main()
{
bar b;
f(std::move(b));
}
Also, whenever copy elision is allowed but does not occur, the move constructor will be used if it is available.

You need to declare g as:
int g(foo && a) //accept argument as rvalue reference
{
return 0;
}
Now it can accept argument by rvalue-reference.
In your case, even though the expression std::move(a) produces rvalue, it doesn't bind to a parameter which accepts argument by value. The receiving end must be rvalue-reference as well.
In case of g(foo()), the copy-elision is performed by the compiler, which is an optimization. It is NOT a requirement by the language[until C++17]. You can disable this optimization if you want to : then g(foo()) and g(std::move(a)) will behave exactly same, as expected.
But if you change g as I suggested above, the call g(foo()) will not make a copy because it is a requirement by the language to not make copy with &&. It is not a compiler-optimization anymore.

Related

Copy constructor implicit conversion problem

Part of the answer is given in here
class foo1
{
private:
int i;
public:
foo1()
{
i=2;
}
int geti(){
return i;
}
};
class foo2
{
private:
int j;
public:
explicit foo2(foo1& obj1) //This ctor doesn't have `const` intentionally as i wanted to understand the behavior.
{
cout<<"\nctor of foo2 invoked";
j=obj1.geti();
}
};
foo1 obj1;
foo2 obj2(obj1); //THIS WORKS
foo2 obj22=obj1; //THIS DOESN'T WORK
Both of them work when the keywork explicit is removed.
Is the following explanation correct for copy initialization( foo2 obj22=obj1) :
At first the compiler creates a temporary object using constructor:
foo2(foo1& other) {}
then it tries to use this temporary object in the copy constructor:
foo2(foo2& other) {}
But it may not bind a temporary object with non-constant reference and it issues an error.
When you are using the equal sign then there is used so-called copy-initialization.
If yes, then shouldn't foo2(foo1& other) {} itself be disallowed before even going further because temporary cannot be bound to non const reference?
Sorry for the long question, my confusion is basically around difference behavior for direct and copy initialization in presence/absence of explicit keyword(with/without const)
Is the following explanation correct for copy initialization
It isn't. You omitted the definition of a copy c'tor for foo2. Which means the compiler synthesizes one for you automatically. The synthesized version will take by a const foo2&. So the issue is not in binding a temporary foo2 to a non-const reference.
As a matter of fact, copy initialization no longer creates temporaries, and doesn't even have to behave as though a temporary is present. What instead happens is that the form of initialization simply takes the explicit keyword into account.
Both of these
foo2 obj2(obj1);
foo2 obj22=obj1;
perform the same overload resolution, which can only choose the same c'tor (foo2(foo1& obj1)). It should choose this c'tor because you pass a non-const lvalue for an argument.
The difference? Direct initialization is allowed to use an explicit c'tor. While copy initialization does not. So your explicit c'tor is not a candidate, and overload resolution fails.

When will c++11 perform move automatically when std::move is not explicitly used?

If I have a struct in which I did not provide any copy and move constructor:
struct MyStruct {
MyStruct() { // this is the only function
...
}
...
};
then if I do the following:
std::vector<MyStruct> vec;
...
vec.push_back(MyStruct());
instead of using std::move() like the followings:
vec.push_back(std::move(MyStruct()));
Will c++11 smartly do the move for my temporary variable? Or, how can I make sure it is a move instead of a copy?
In C++11 std::vector::push_back will use a move constructor if passed an rvalue (and a move constructor exists for the type), but you should also consider using std::vector::emplace_back in such situations; std::vector::emplace_back will construct the object in place rather than moving it.
Will c++11 smartly do the move for my temporary variable? Or, how can I make sure it is a move instead of a copy?
It depends. This
vec.push_back(MyStruct());
will bind to
std::vector<MyStruct>::push_back(MyStruct&&);
but whether the rvalue passed is moved or copied depends fully on whether MyStruct has a move copy constructor (likewise for move assignment).
It will make absolutely no difference if you call
vec.push_back(std::move(MyStruct()));
because MyStruct() is already an rvalue.
So it really depends on the details of MyStruct. There is simply not enough information in your question to know if your class has move constructor.
These are the conditions that must be met for a class to have an implicitly generated move constructor:
no user-declared copy constructors
no user-declared copy assignment operators
no user-declared move assignment operators
no user-declared destructors
Of course, you can always provide your own if any of these conditions are not met:
MyStruct(MyStruct&&) = default;
Because the MyStruct() will create an rvalue the T && overload will be called.
It's actually very easy to verify (demo):
#include <iostream>
struct A{ int x; };
void foo(A &&x){ std::cout<<"&&" <<std::endl; }
void foo(A &x){ std::cout<<"&" <<std::endl; }
int main() {
foo(A()); // prints &&
A a;
foo(a); // prints &
return 0;
}
To clarify: I didn't mention anything about move constructor because one can have an explicitly deleted move constructor and still the T && will be invoked.
E.g (demo):
#include <iostream>
struct A{ int x; A() = default; A(const A& ) = default; A(A&&) = delete; };
/* ^
no move ctor */
void foo(A &&x){ std::cout<<"&&" <<std::endl; }
void foo(A &x){ std::cout<<"&" <<std::endl; }
int main() {
foo(A()); //still prints &&
A a;
foo(a);
return 0;
}
As I said before this is because it's an rvalue...
Yes, it is going to use push_back( T&& value ), and move the value.
If the type is movable then it definitely will be. In general standard-complying compiler should always choose move semantics to copy semantics if such are available.

Returning a value from a function with move semantics or the return value optimization, but not the copy constructor

Is there a good way to return a value from a function in C++ where we guarantee that the copy constructor is not called? Either the return value optimization or the move constructor are fine. For example, with the following code
#include <iostream>
struct Foo {
private:
// Disallow the copy and default constructor as well as the assignment
// operator
Foo();
Foo(Foo const & foo);
Foo & operator = (Foo const & foo);
public:
// Store a little bit of data
int data;
Foo(int const & data_) : data(data_) { }
// Write a move constructor
Foo(Foo && foo) {
std::cout << "Move constructor" << std::endl;
data=foo.data;
}
};
// Write a function that creates and returns a Foo
Foo Bar() {
Foo foo(3);
return foo;
}
// See if we can mix things up
Foo Baz(int x) {
Foo foo2(2);
Foo foo3(3);
return x>2 ? foo2 : foo3;
}
int main() {
// This is using the return value optimization (RVO)
Foo foo1 = Bar();
std::cout << foo1.data << std::endl;
// This should make the RVO fail
Foo foo2 = Baz(3);
std::cout << foo2.data << std::endl;
}
We have a compiler error
$ make
g++ -std=c++11 test01.cpp -o test01
test01.cpp: In function 'Foo Baz(int)':
test01.cpp:10:5: error: 'Foo::Foo(const Foo&)' is private
test01.cpp:35:25: error: within this context
make: *** [all] Error 1
since the copy constructor is private. Now, if we modify the Baz function to
// See if we can mix things up
Foo Baz(int x) {
Foo foo2(2);
Foo foo3(3);
return std::move(x>2 ? foo2 : foo3);
}
we do in fact run correctly. However, this seems to preclude the RVO from being used ever. Is there a better way to structure these functions if we must guarantee that the copy constructor is not called?
From the C++ standard:
[class.copy]/31: When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object, even if the copy/move constructor and/or destructor for the object have side effects. ... This elision of copy/move operations, called copy elision, 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 (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
Since x > 2 ? foo2 : foo3 is not the name of an automatic object, copy elision is not permitted.
Interestingly, your example is addressed in n1377:
With this language feature in place, move/copy elision, although still
important, is no longer critical. There are some functions where NRVO
is allowed, but can be exceedingly difficult to implement. For
example:
A
f(bool b)
{
A a1, a2;
// ...
return b ? a1 : a2;
}
It is somewhere between difficult and impossible to decide whether to
construct a1 or a2 in the caller's preferred location. Using A's move
constructor (instead of copy constructor) to send a1 or a2 back to the
caller is the best solution.
We could require that the author of operator+ explicitly request the
move semantics. But what would be the point? The current language
already allows for the elision of this copy, so the coder already can
not rely on destruction order of the local, nor can he rely on the
copy constructor being called. The auto-local is about to be
conceptually destructed anyway, so it is very "rvalue-like". The move
is not detectable except by measuring performance, or counting copies
(which may be elided anyway).
Note that this language addition permits movable, but non-copyable
objects (such as move_ptr) to be returned by value, since a move
constructor is found and used (or elided) instead of the inaccessible
copy constructor.
Their example of solving this (in favor of move semantics) is:
// Or just call std::move
// return x>2 ? static_cast<Foo&&>(foo2) : static_cast<Foo&&>(foo3);
return static_cast<Foo&&>(x>2 ? foo2 : foo3);
The logic resulting from this implicit cast results in an automatic
hierarchy of "move semantics" from best to worst:
If you can elide the move/copy, do so (by present language rules)
Else if there is a move constructor, use it
Else if there is a copy constructor, use it
Else the program is ill formed
Or as Xeo mentions, you can structure it this way:
Foo Baz(int x) {
Foo foo2(2);
Foo foo3(3);
if (x > 2)
return foo2;
else
return foo3;
}
You already provided an example in the OP, but the standard provides one for eliding the move/copy constructor (it equally applies):
class Thing {
public:
Thing() { }
~Thing() { }
Thing(Thing&& thing) {
std::cout << "hi there";
}
};
Thing f() {
Thing t;
return t;
}
Thing t2 = f();
// does not print "hi there"
But if you supply both the move and copy constructor, the move constructor seems to be preferred.

When Does Move Constructor get called?

I'm confused about when a move constructor gets called vs a copy constructor.
I've read the following sources:
Move constructor is not getting called in C++0x
Move semantics and rvalue references in C++11
msdn
All of these sources are either overcomplicated(I just want a simple example) or only show how to write a move constructor, but not how to call it. Ive written a simple problem to be more specific:
const class noConstruct{}NoConstruct;
class a
{
private:
int *Array;
public:
a();
a(noConstruct);
a(const a&);
a& operator=(const a&);
a(a&&);
a& operator=(a&&);
~a();
};
a::a()
{
Array=new int[5]{1,2,3,4,5};
}
a::a(noConstruct Parameter)
{
Array=nullptr;
}
a::a(const a& Old): Array(Old.Array)
{
}
a& a::operator=(const a&Old)
{
delete[] Array;
Array=new int[5];
for (int i=0;i!=5;i++)
{
Array[i]=Old.Array[i];
}
return *this;
}
a::a(a&&Old)
{
Array=Old.Array;
Old.Array=nullptr;
}
a& a::operator=(a&&Old)
{
Array=Old.Array;
Old.Array=nullptr;
return *this;
}
a::~a()
{
delete[] Array;
}
int main()
{
a A(NoConstruct),B(NoConstruct),C;
A=C;
B=C;
}
currently A,B,and C all have different pointer values. I would like A to have a new pointer, B to have C's old pointer, and C to have a null pointer.
somewhat off topic, but If one could suggest a documentation where i could learn about these new features in detail i would be grateful and would probably not need to ask many more questions.
A move constructor is called:
when an object initializer is std::move(something)
when an object initializer is std::forward<T>(something) and T is not an lvalue reference type (useful in template programming for "perfect forwarding")
when an object initializer is a temporary and the compiler doesn't eliminate the copy/move entirely
when returning a function-local class object by value and the compiler doesn't eliminate the copy/move entirely
when throwing a function-local class object and the compiler doesn't eliminate the copy/move entirely
This is not a complete list. Note that an "object initializer" can be a function argument, if the parameter has a class type (not reference).
a RetByValue() {
a obj;
return obj; // Might call move ctor, or no ctor.
}
void TakeByValue(a);
int main() {
a a1;
a a2 = a1; // copy ctor
a a3 = std::move(a1); // move ctor
TakeByValue(std::move(a2)); // Might call move ctor, or no ctor.
a a4 = RetByValue(); // Might call move ctor, or no ctor.
a1 = RetByValue(); // Calls move assignment, a::operator=(a&&)
}
First of all, your copy constructor is broken. Both the copied from and copied to objects will point to the same Array and will both try to delete[] it when they go out of scope, resulting in undefined behavior. To fix it, make a copy of the array.
a::a(const a& Old): Array(new int[5])
{
for( size_t i = 0; i < 5; ++i ) {
Array[i] = Old.Array[i];
}
}
Now, move assignment is not being performed as you want it to be, because both assignment statements are assigning from lvalues, instead of using rvalues. For moves to be performed, you must be moving from an rvalue, or it must be a context where an lvalue can be considered to be an rvalue (such as the return statement of a function).
To get the desired effect use std::move to create an rvalue reference.
A=C; // A will now contain a copy of C
B=std::move(C); // Calls the move assignment operator
Remember that copy elision could occur. If you disable it by passing the -fno-elide-constructors flag to the compiler your constructor might get executed.
You can read about it here: https://www.geeksforgeeks.org/copy-elision-in-c/
Answers above do not give a 'natural' example when a move constructor is called. I found this way to call move constructor without std::move (and without suppressing copy elision by -fno-elide-constructors):
a foo(a a0) {
return a0; // move ctor is called
}
a a1 = foo(a());

C++ Copy Constructor

I have a question about this syntax regarding initialization.
Quoted from http://en.wikipedia.org/wiki/Copy_constructor
X a = X();
// valid given X(const X& copy_from_me) but not valid given X(X& copy_from_me)
// because the second wants a non-const X&
// to create a, the compiler first creates a temporary by invoking the default constructor
// of X, then uses the copy constructor to initialize a as a copy of that temporary.
// However, for some compilers both the first and the second actually work.
#include <iostream>
class Foo
{
public:
Foo()
{
std::cout << "Default Constructor called" << std::endl;
}
Foo(const Foo& other)
{
std::cout << "Copy constructor called" << std::endl;
}
Foo& operator=(const Foo& rhs)
{
std::cout << "Assignment operator called" << std::endl;
}
};
int main()
{
Foo b = Foo(); //case 1:default
Foo c = Foo(a); //case 2: copy constructor
}
Case 1:
Upon changing the parameter from const to non const in the copy constructor, case 1 won't compile as expected from wikipedia. However, when ran using the proper copy constructor, it only calls the default constructor. Why doesn't it also call the copy constructor? Is this an optimization done at compile-time?
Case 2:
The answer to case 1 will probably answer case 2 for me, but why does this only call the copy constructor once?
Foo b = Foo();
This form requires a valid matching copy constructor to exist, but the copy may be optimized away. The fact that it may be optimized away does not relax the requirement that the constructor exist though.
By making your copy constructor take a non-const reference, it no longer matches, since Foo() generates a temporary, and temporaries cannot bind to non-const references. When you make the parameter const reference(or scrap your copy c-tor and use the compiler generated copy c-tor), then it works, because temporaries can bind to const references.
X() is a temporary, so you can't bind it to a non-const reference (although MSVS has an extension that allows it).
1) Yes, it's a compiler optimization
2) Illegal, because a doesn't exist. But in principle, again, yes, a compiler optimization.