Is my compiler optimizing away the rvalue returned from function? [duplicate] - c++11

What is copy elision? What is (named) return value optimization? What do they imply?
In what situations can they occur? What are limitations?
If you were referenced to this question, you're probably looking for the introduction.
For a technical overview, see the standard reference.
See common cases here.

Introduction
For a technical overview - skip to this answer.
For common cases where copy elision occurs - skip to this answer.
Copy elision is an optimization implemented by most compilers to prevent extra (potentially expensive) copies in certain situations. It makes returning by value or pass-by-value feasible in practice (restrictions apply).
It's the only form of optimization that elides (ha!) the as-if rule - copy elision can be applied even if copying/moving the object has side-effects.
The following example taken from Wikipedia:
struct C {
C() {}
C(const C&) { std::cout << "A copy was made.\n"; }
};
C f() {
return C();
}
int main() {
std::cout << "Hello World!\n";
C obj = f();
}
Depending on the compiler & settings, the following outputs are all valid:
Hello World!
A copy was made.
A copy was made.
Hello World!
A copy was made.
Hello World!
This also means fewer objects can be created, so you also can't rely on a specific number of destructors being called. You shouldn't have critical logic inside copy/move-constructors or destructors, as you can't rely on them being called.
If a call to a copy or move constructor is elided, that constructor must still exist and must be accessible. This ensures that copy elision does not allow copying objects which are not normally copyable, e.g. because they have a private or deleted copy/move constructor.
C++17: As of C++17, Copy Elision is guaranteed when an object is returned directly:
struct C {
C() {}
C(const C&) { std::cout << "A copy was made.\n"; }
};
C f() {
return C(); //Definitely performs copy elision
}
C g() {
C c;
return c; //Maybe performs copy elision
}
int main() {
std::cout << "Hello World!\n";
C obj = f(); //Copy constructor isn't called
}

Common forms of copy elision
For a technical overview - skip to this answer.
For a less technical view & introduction - skip to this answer.
(Named) Return value optimization is a common form of copy elision. It refers to the situation where an object returned by value from a method has its copy elided. The example set forth in the standard illustrates named return value optimization, since the object is named.
class Thing {
public:
Thing();
~Thing();
Thing(const Thing&);
};
Thing f() {
Thing t;
return t;
}
Thing t2 = f();
Regular return value optimization occurs when a temporary is returned:
class Thing {
public:
Thing();
~Thing();
Thing(const Thing&);
};
Thing f() {
return Thing();
}
Thing t2 = f();
Other common places where copy elision takes place is when an object is constructed from a temporary:
class Thing {
public:
Thing();
~Thing();
Thing(const Thing&);
};
void foo(Thing t);
Thing t2 = Thing();
Thing t3 = Thing(Thing()); // two rounds of elision
foo(Thing()); // parameter constructed from temporary
or when an exception is thrown and caught by value:
struct Thing{
Thing();
Thing(const Thing&);
};
void foo() {
Thing c;
throw c;
}
int main() {
try {
foo();
}
catch(Thing c) {
}
}
Common limitations of copy elision are:
multiple return points
conditional initialization
Most commercial-grade compilers support copy elision & (N)RVO (depending on optimization settings). C++17 makes many of the above classes of copy elision mandatory.

Standard reference
For a less technical view & introduction - skip to this answer.
For common cases where copy elision occurs - skip to this answer.
Copy elision is defined in the standard in:
12.8 Copying and moving class objects [class.copy]
as
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. In such cases,
the implementation treats the source and target of the omitted copy/move 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.123 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 cvunqualified
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
— in a throw-expression, when the operand is the name of a non-volatile automatic object (other than a
function or catch-clause parameter) whose scope does not extend beyond the end of the innermost
enclosing try-block (if there is one), the copy/move operation from the operand to the exception
object (15.1) can be omitted by constructing the automatic object directly into the exception object
— when a temporary class object that has not been bound to a reference (12.2) would be copied/moved
to a class object with the same cv-unqualified type, the copy/move operation can be omitted by
constructing the temporary object directly into the target of the omitted copy/move
— when the exception-declaration of an exception handler (Clause 15) declares an object of the same type
(except for cv-qualification) as the exception object (15.1), the copy/move operation can be omitted
by treating the exception-declaration as an alias for the exception object if the meaning of the program
will be unchanged except for the execution of constructors and destructors for the object declared by
the exception-declaration.
123) Because only one object is destroyed instead of two, and one copy/move constructor is not executed, there is still one
object destroyed for each one constructed.
The example given is:
class Thing {
public:
Thing();
~Thing();
Thing(const Thing&);
};
Thing f() {
Thing t;
return t;
}
Thing t2 = f();
and explained:
Here the criteria for elision can be combined to eliminate two calls to the copy constructor of class Thing:
the copying of the local automatic object t into the temporary object for the return value of function f()
and the copying of that temporary object into object t2. Effectively, the construction of the local object t
can be viewed as directly initializing the global object t2, and that object’s destruction will occur at program
exit. Adding a move constructor to Thing has the same effect, but it is the move construction from the
temporary object to t2 that is elided.

Copy elision is a compiler optimization technique that eliminates unnecessary copying/moving of objects.
In the following circumstances, a compiler is allowed to omit copy/move operations and hence not to call the associated constructor:
NRVO (Named Return Value Optimization): If a function returns a class type by value and the return statement's expression is the name of a non-volatile object with automatic storage duration (which isn't a function parameter), then the copy/move that would be performed by a non-optimising compiler can be omitted. If so, the returned value is constructed directly in the storage to which the function's return value would otherwise be moved or copied.
RVO (Return Value Optimization): If the function returns a nameless temporary object that would be moved or copied into the destination by a naive compiler, the copy or move can be omitted as per 1.
#include <iostream>
using namespace std;
class ABC
{
public:
const char *a;
ABC()
{ cout<<"Constructor"<<endl; }
ABC(const char *ptr)
{ cout<<"Constructor"<<endl; }
ABC(ABC &obj)
{ cout<<"copy constructor"<<endl;}
ABC(ABC&& obj)
{ cout<<"Move constructor"<<endl; }
~ABC()
{ cout<<"Destructor"<<endl; }
};
ABC fun123()
{ ABC obj; return obj; }
ABC xyz123()
{ return ABC(); }
int main()
{
ABC abc;
ABC obj1(fun123()); //NRVO
ABC obj2(xyz123()); //RVO, not NRVO
ABC xyz = "Stack Overflow";//RVO
return 0;
}
**Output without -fno-elide-constructors**
root#ajay-PC:/home/ajay/c++# ./a.out
Constructor
Constructor
Constructor
Constructor
Destructor
Destructor
Destructor
Destructor
**Output with -fno-elide-constructors**
root#ajay-PC:/home/ajay/c++# g++ -std=c++11 copy_elision.cpp -fno-elide-constructors
root#ajay-PC:/home/ajay/c++# ./a.out
Constructor
Constructor
Move constructor
Destructor
Move constructor
Destructor
Constructor
Move constructor
Destructor
Move constructor
Destructor
Constructor
Move constructor
Destructor
Destructor
Destructor
Destructor
Destructor
Even when copy elision takes place and the copy-/move-constructor is not called, it must be present and accessible (as if no optimization happened at all), otherwise the program is ill-formed.
You should permit such copy elision only in places where it won’t affect the observable behavior of your software. Copy elision is the only form of optimization permitted to have (i.e. elide) observable side-effects. Example:
#include <iostream>
int n = 0;
class ABC
{ public:
ABC(int) {}
ABC(const ABC& a) { ++n; } // the copy constructor has a visible side effect
}; // it modifies an object with static storage duration
int main()
{
ABC c1(21); // direct-initialization, calls C::C(42)
ABC c2 = ABC(21); // copy-initialization, calls C::C( C(42) )
std::cout << n << std::endl; // prints 0 if the copy was elided, 1 otherwise
return 0;
}
Output without -fno-elide-constructors
root#ajay-PC:/home/ayadav# g++ -std=c++11 copy_elision.cpp
root#ajay-PC:/home/ayadav# ./a.out
0
Output with -fno-elide-constructors
root#ajay-PC:/home/ayadav# g++ -std=c++11 copy_elision.cpp -fno-elide-constructors
root#ajay-PC:/home/ayadav# ./a.out
1
GCC provides the -fno-elide-constructors option to disable copy elision.
If you want to avoid possible copy elision, use -fno-elide-constructors.
Now almost all compilers provide copy elision when optimisation is enabled (and if no other option is set to disable it).
Conclusion
With each copy elision, one construction and one matching destruction of the copy are omitted, thus saving CPU time, and one object is not created, thus saving space on the stack frame.

Here I give another example of copy elision that I apparently encountered today.
# include <iostream>
class Obj {
public:
int var1;
Obj(){
std::cout<<"In Obj()"<<"\n";
var1 =2;
};
Obj(const Obj & org){
std::cout<<"In Obj(const Obj & org)"<<"\n";
var1=org.var1+1;
};
};
int main(){
{
/*const*/ Obj Obj_instance1; //const doesn't change anything
Obj Obj_instance2;
std::cout<<"assignment:"<<"\n";
Obj_instance2=Obj(Obj(Obj(Obj(Obj_instance1)))) ;
// in fact expected: 6, but got 3, because of 'copy elision'
std::cout<<"Obj_instance2.var1:"<<Obj_instance2.var1<<"\n";
}
}
With the result:
In Obj()
In Obj()
assignment:
In Obj(const Obj & org)
Obj_instance2.var1:3

Related

move constructor called on return instead of copy

Today I found that this code doesn't work as I expect it to work.
According to my knowledg for L-values copy constructor should be called while for R-values it should choose move constructor. Otherwise what's the purpose of std::move which actually does nothing but casts to R-value. I was expecting that return obj will call copy constructor, but it calls move.
I understand that copy is useless here, but that's about rules. What if my copy constructor has a side effect and that's my case (I know that it shouldn't, but technically it can - for example here: std::cout call).
Is there anything in standard that allows such behavior? Also how can I force a copy?
#include <iostream>
class X
{
public:
X() = default;
X(const X& r): i(r.i)
{
std::cout << "copy ctor" << std::endl;
}
X(const X&& r): i(r.i)
{
std::cout << "move ctor" << std::endl;
}
int i = 0;
};
X foo()
{
X obj;
obj.i = 10;
return obj;
}
int main()
{
X x = foo();
}
move ctor
move ctor
From cppreference (emphasis mine):
If [the returned expression] is an lvalue expression and the conditions for copy elision are met, or would be met, except that [the returned expression] names a function parameter, then overload resolution to select the constructor to use for initialization of the returned value is performed twice: first as if [the returned expression] were an rvalue expression (thus it may select the move constructor or a copy constructor taking reference to const), and if no suitable conversion is available, overload resolution is performed the second time, with lvalue [returned expression] (so it may select the copy constructor taking a reference to non-const).
The above rule applies even if the function return type is different from the type of [the returned expression] (copy elision requires same type)
Long story short, return implicitly tries to move what you return when it makes sense. It will only copy as a last resort (no move constructor available, for example).

C++ default move operation in "Effective Modern C++"

Item 17: Understand special member function generation.
Move operations are generated only for classed lacking
explicitly declared move operation, copy operations,
or a destructor.
Now, when I refer to a move operation move-constructing
or move-assigning a data member or base class, there
is no guarantee that a move will actually take place.
"Memberwise moves" are, in reality, more like memberwise
move requests, because types that aren't move-enabled(...)
will be "moved" via their copy operations.
However, I can not verify them on my environment.
// compiled
#include <iostream>
using namespace std;
class Base {
public:
~Base() {}
};
int main() {
Base a, c;
Base b(move(a));
c = move(b);
// explicitly destructor does not disable
// default move constuctor and move assignment operator
return 0;
}
class Base {
public:
Base() {}
Base(Base& b) {}
~Base() {}
};
class Num {
private:
Base b;
};
int main() {
Num a, c;
c = move(a); // passed
Num b(move(c)); // error
// explicitly Base::Base(Base& b) disable default move
// move conctructor.
// Num's default move constructor can not find any move
// constructor for member object Base b, which lead to an
// error. Num's default move constructor does not "moved"
// Base b via their copy operations which is declared.
return 0;
}
The first assertion might be vary from different environments, but the second one is almost wrong.
I am very confusing about it.
Please help me out.
class Base {
public:
~Base() {}
};
Because Base has a user-declared destructor, it does not have a move constructor or move assignment operator at all. The lines
Base b(move(a));
c = move(b);
actually call the copy constructor and copy assignment operator, respectively.
class Base {
public:
Base() {}
Base(Base& b) {}
~Base() {}
};
class Num {
private:
Base b;
};
Again, Base has no move constructor at all. However, Num does have an implicitly-declared move constructor, since Num itself does not declare any special member functions. However, it is implicitly defined as deleted, since the default definition would be ill-formed:
Num::Num(Num&& n) : b(std::move(n.b)) {}
// Cannot convert rvalue of type `Base` to `Base&`
// for the copy constructor to be called.
Note that the "move" of b does attempt to use the copy constructor, but it can't.

C++ Do I always have to use std::move to invoke the move constructor?

"Theory" question if you will.
In order to execute/make use of the move constructor in a class, do I always have to use std::move(...) to tell the compiler that I wish to 'move' an object rather than copy it?
Are there any cases where the compiler will invoke the move constructor for me without the use of std::move? (My guess would be in function return values?)
According to cppreference.com (http://en.cppreference.com/w/cpp/language/move_constructor):
The move constructor is called whenever an object is initialized from xvalue of the same type, which includes
initialization, T a = std::move(b); or T a(std::move(b));, where b is of type T;
function argument passing: f(std::move(a));, where a is of type T and f is void f(T t);
function return: return a; inside a function such as T f(), where a is of type T which has a move constructor.
In most cases, yes std::move is needed.
The compiler will invoke the move constructor without std::move when:
returning a local variable by value
when constructing an object from an rvalue of the same type
In all other cases, use std::move. E.g.:
struct S {
std::string name;
S(std::string name) : name(std::move(name)) {}
};
and
std::unique_ptr<Base> func() {
auto p = std::make_unique<Derived>();
return std::move(p); // doesn't work without std::move
}
std::move is just a cast.
unique_ptr<int> global;
auto v = unique_ptr<int>(global); // global is a lvalue, therefore the
unique_ptr(unique_ptr<T>&v) constructor that accepts lvalue references is called.
auto v = unique_ptr<int>(std::move(global)); // move returns a &&rvalue reference, therefore the
unique_ptr(unique_ptr<T>&&v) constructor that accepts &&rvalue references is used.
When the criteria for elision of a copy operation are met and the object to be copied is designated by an lvalue, overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue.
therefore,
unique_ptr<int> hello()
{
unique_ptr<int> local;
return local;
// local is an lvalue, but since the critera for elision is met,
// the returned object is created using local as if it was an rvalue
}
Also,
unique_ptr<int> hello = std::unique_ptr<int>();
// we have a pure rvalue in the right, therefore no std::move() cast is needed.

C++11 implicit copy constructor while implementing explicitly a constructor

I ran into a problem. I implemented a constructor for a class, but why are implicitly generated the other constructors, like the copy one?
I thought, that if I define a constructor explicitly, then the compiler doesn't generates implicitly other ones. I'm really hoping, that this a VC++ specific thing, and that this code doesn't conforms to ISO:IEC C++11:
class Foo
{
int bar;
public:
Foo(int&& arg) : bar(arg) { cout << "RConstruction" << endl; }
};
int main(int, const char*[])
{
Foo f = Foo(42);
/* Create unused temporary on the stack */
Foo::Foo(f); /* calling Foo::Foo(const Foo&): this shouldn't work... */
return (0);
}
Please keep in mind, that this is a sample code, created exactly for this situation, for demonstration purposes, I expect answers only that strictly relate to this question.
That's not a move constructor, so it doesn't suppress any implicit ones.
Just like Foo(const int&) isn't a copy constructor, Foo(int&&) isn't a move constructor, it's just a constructor taking an rvalue reference.
A move constructor looks like one of:
Foo(Foo&&)
Foo(const Foo&&)
Foo(volatile Foo&&)
Foo(const volatile Foo&&)
I thought, that if I define a constructor explicitly, then the compiler doesn't generates implicitly other ones.
If you define any constructor the compiler doesn't generate the default constructor, but it still generates the others. Define the as deleted if you don't want them:
Foo(const Foo&) = delete;
Foo& operator=(const Foo&) = delete;
You did not declare a move constructor, but a regular constructor : no implicit constructor will be deleted.
A move constructor would be of the form Foo(Foo&& arg) (with any cv-qualifier on the argument)
Also note that this statement is not valid C++ :
Foo::Foo(f);
Maybe you meant :
Foo g = Foo(f);

Initialization of member array objects avoiding move constructor

I'm trying to create and initialize a class that contains a member array of a non-trivial class, which contains some state and (around some corners) std::atomic_flag. As of C++11 one should be able to initialize member arrays.
The code (stripped down to minimum) looks like this:
class spinlock
{
std::atomic_flag flag;
bool try_lock() { return !flag.test_and_set(std::memory_order_acquire); }
public:
spinlock() : flag(ATOMIC_FLAG_INIT){};
void lock() { while(!try_lock()) ; }
void unlock() { flag.clear(std::memory_order_release); }
};
class foo
{
spinlock lock;
unsigned int state;
public:
foo(unsigned int in) : state(in) {}
};
class bar
{
foo x[4] = {1,2,3,4}; // want each foo to have different state
public:
//...
};
If I understand the compiler output correctly, this seems not to construct the member array, but to construct temporaries and invoke the move/copy constructor, which subsequently calls move constructors in sub-classes, and that one happens to be deleted in std::atomic_flag. The compiler output that I get (gcc 4.8.1) is:
[...] error: use of deleted function 'foo::foo(foo&&)'
note: 'foo::foo(foo&&)' is implicitly deleted because the default definition would be ill-formed
error: use of deleted function 'spinlock::spinlock(spinlock&&)'
note: 'spinlock::spinlock(spinlock&&)' is implicitly deleted because [...]
error: use of deleted function 'std::atomic_flag::atomic_flag(const std::atomic_flag&)'
In file included from [...]/i686-w64-mingw32/4.8.1/include/c++/atomic:41:0
[etc]
If I remove the array and instead just put a single foo member inside bar, I can properly initialize it using standard constructor initializers, or using the new in-declaration initialization, no problem whatsoever. Doing the same thing with a member array fails with the above error, no matter what I try.
I don't really mind that array elements are apparently constructed as temporaries and then moved rather than directly constructed, but the fact that it doesn't compile is obviously somewhat of a showstopper.
Is there a way I either force the compiler to construct (not move) the array elements, or a way I can work around this?
Here's a minimal example exposing the problem:
struct noncopyable
{
noncopyable(int) {};
noncopyable(noncopyable const&) = delete;
};
int main()
{
noncopyable f0 = {1};
noncopyable f1 = 1;
}
Although the two initializations of f0 and f1 have the same form (are both copy-initialization), f0 uses list-initialization which directly calls a constructor, whereas the initialization of f1 is essentially equivalent to foo f1 = foo(1); (create a temporary and copy it to f1).
This slight difference also manifests in the array case:
noncopyable f0[] = {{1}, {2}, {3}, {4}};
noncopyable f1[] = {1, 2, 3, 4};
Aggregate-initialization is defined as copy-initialization of the members [dcl.init.aggr]/2
Each member is copy-initialized from the corresponding initializer-clause.
Therefore, f1 essentially says f1[0] = 1, f1[1] = 2, .. (this notation shall describe the initializations of the array elements), which has the same problem as above. OTOH, f0[0] = {1} (as an initialization) uses list-initialization again, which directly calls the constructor and does not (semantically) create a temporary.
You could make your converting constructors explicit ;) this could avoid some confusion.
Edit: Won't work, copy-initialization from a braced-init-list may not use an explicit constructor. That is, for
struct expl
{
explicit expl(int) {};
};
the initialization expl e = {1}; is ill-formed. For the same reason, expl e[] = {{1}}; is ill-formed. expl e {1}; is well-formed, still.
Gcc refuses to compile list-initialization of arrays of objects with virtual destructor up to version 10.2. In 10.3 that was fixed.
E.g. if noncopyable from #dyp answer had a virtual destructor, gcc fails to compile line:
noncopyable f0[] = {{1}, {2}, {3}, {4}};
arguing to deleted copy and move c-rs. But successfully compiles under 10.3 and higher.

Resources