Assuming no compiler optimization, how many times will this object be created? - c++

Assuming there is no compiler optimization. How many times would OutputBuffer_s type object will be created?
#include <iostream>
#include <vector>
struct OutputBuffer_s {
int encoded[10];
};
OutputBuffer_s func() {
OutputBuffer_s s;
return s;
}
int main() {
OutputBuffer_s a = func();
}
Initially, I had assumed three times.
1) When func() is called, object s will be created on stack.
2) When func() goes out of scope, it will return copy of object s to main().
3) Copying of value to object a in main(), since value returned by func() would be a temporary.
I know that I'm wrong here, since I compiled with -O0 in g++ but I could see only one creation after overriding the constructors. I want to know where and why I am wrong.

What you have here copy-elison.
Omits copy and move (since C++11) constructors, resulting in zero-copy pass-by-value semantics.
GCC can elide the constructors even with -O0 option. This is what is happening here. If you want to specifically prevent elision, you can use the -fno-elide-constructors option.
If you use this option, there will be one constructor call and two move constructor calls for C++11.
See demo here.
If you use C++17, there is guaranteed copy-elision in some cases, and here even with the -fno-elide-constructors option, there will be one constructor call and just one move constructor call.
See demo here.

C++17 has introduced Temporary materialization which I quote:
A prvalue of any complete type T can be converted to an xvalue of the same type T. This conversion initializes a temporary object of type T from the prvalue by evaluating the prvalue with the temporary object as its result object, and produces an xvalue denoting the temporary object. If T is a class or array of class type, it must have an accessible and non-deleted destructor.
In that case the extra calls to the contructor will become a move operation. Prior to C++17, which copy elision was not mandatory, the compiler would usually copy elide. As far as I am aware, in your case, a compiler would copy elide anyway (try godbolt and check the produced assembly).
To fully answer, one call to the constructor and one move.

Related

copy elision in c++03

Copy-elision is, in some cases, mandatory in c++17, and permitted in c++11/14. This in particular concerns copy initialization.
For example, the following program
#include <iostream>
struct A
{
explicit A(int){ std::cout << "conversion" << std::endl; }
A(const A&) { std::cout << "copy constructor" << std::endl; }
};
int main()
{
A b = A(3);
}
is expected in c++17 to produce an output
conversion
and in c++11/14 may produce the same output. With these regards, both gcc 10.1.0 and clang 11.1.0 produce the output above also with -std=c++11 or -std=c++14, unless one explicitly disables the optional constructors elision with -fno-elide-constructors.
But what about c++03 standard? Was it allowed to elide the copy constructors in the copy initialization? gcc and clang with -std=c++03 always suppress the copy constructor (unless one specifies -fno-elide-constructors).
Yes, copy ellision is permitted in C++03 and C++98. That's the paragraph for C++98 and C++03:
Non-mandatory elision of copy operations
Under the following circumstances, the compilers are permitted, but
not required to omit the copy construction of
class objects even if the copy constructor and the
destructor have observable side-effects. The objects are constructed
directly into the storage where they would otherwise be copied
to. This is an optimization: even when it takes place and the
copy constructor is not called, it still must be
present and accessible (as if no optimization happened at all),
otherwise the program is ill-formed:
In a return statement, when the operand is the name of a non-volatile object with automatic storage duration, which isn't a
function parameter or a catch clause parameter, and which is of the
same class type (ignoring cv-qualification) as the function return
type. This variant of copy elision is known as NRVO, "named return
value optimization".
In the initialization of an object, when the source object is a nameless temporary and is of the same class type (ignoring
cv-qualification) as the target object. When the nameless temporary is
the operand of a return statement, this variant of copy elision is
known as RVO, "return value optimization".
When copy elision occurs, 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
cppreference
I removed everything that's only valid since C++11.
The only differences between C++98, C++03 and C++11 regarding ellision are move operations and exception handling.

C++ copy constructor activation in return-by-vlaue

I wasn't able to find a concrete answer for the following question:
Consider the following code:
Obj f() {
Obj o2;
return o2;
}
int main() {
Obj o1 = f();
return 0;
}
How many times is the copy constructor activated without compiler optimization?
In case there isn't move constructor, isn't it one time for copying o2 to the caller function and another time for constructing o1?
In case there is move constructor, isn't it one time for copying o2 to the caller function and another time for constructing o1 (2nd time is move const)?
C++03 and before
Obj is copied twice. Once by the return statement (constructing the return value), and once to initialize o1 by copying the return value.
C++11 and C++14
If Obj has a usable move constructor, it is moved twice and copied zero times. The return statement must use a move, even though the returned expression is an lvalue. This "move optimization" is mandatory due to a special rule in the language; o2 must not be copied, even when optimizations are disabled. A second move occurs when o1 is initialized.
If Obj has either no move constructor or an implicitly deleted move constructor, the copy constructor is used twice.
If Obj has an explicitly deleted move constructor, the program is ill-formed since the initialization of o1 tries to use the deleted move constructor.
C++17 and later
If Obj has a usable move constructor, it is moved once, when the return statement is executed. As mentioned above, it is mandatory for the compiler to use a move instead of a copy. The construction of o1 involves neither a copy nor a move. Rather, the return statement in f() initializes o1, without the involvement of a temporary. This is because of "guaranteed copy elision": the language requires the copy to be elided, even if optimizations are disabled. This is because f() is a prvalue, and a prvalue is not materialized (i.e., instantiated as a temporary object) unless it is necessary to do so. The "legal fiction" created by the standard is that f() actually returns a "recipe" for creating Obj, not an Obj itself. In practice, this can be implemented the same way that the (optional) return value optimization was implemented in earlier versions of the standard: the caller passes a pointer to o1 directly into f, and the return statement constructs Obj into this pointer.
If the move constructor of Obj is implicitly deleted or does not exist, the copy constructor will be used by the return statement, so there will be one copy and zero moves.
If the move constructor of Obj is explicitly deleted, the program is ill-formed as in the C++11/C++14 case.
In all cases
The copies/moves in the above situations can be optimized out. In cases that involve more than one copy/move operation, the compiler can optimize out any or all of them.
Before C++17, yes there are two copy constructor calls (both of which can be elided even if they have side-effects). You can see this with -fno-elide-constructors in gcc/clang.
Since C++17 due to the new temporary materialization rules there is just one copy involved inside f (which, again, can be elided).
To be accurate all of them are moves, not copies.

Why only call constructor one time? [duplicate]

Here is the little code snippet:
class A
{
public:
A(int value) : value_(value)
{
cout <<"Regular constructor" <<endl;
}
A(const A& other) : value_(other.value_)
{
cout <<"Copy constructor" <<endl;
}
private:
int value_;
};
int main()
{
A a = A(5);
}
I assumed that output would be "Regular Constructor" (for RHS) followed by "Copy constructor" for LHS. So I avoided this style and always declared variable of class as A a(5);. But to my surprise in the code above copy constructor is never called (Visual C++ 2008)
Does anybody know if this behavior is a result of compiler optimization, or some documented (and portable) feature of C++? Thanks.
From another comment: "So by default I should not rely on it (as it may depend on the compiler)"
No, it does not depend on the compiler, practically anyway. Any compiler worth a grain of sand won't waste time constructing an A, then copying it over.
In the standard it explicitly says that it is completely acceptable for T = x; to be equivalent to saying T(x);. (§12.8.15, pg. 211) Doing this with T(T(x)) is obviously redundant, so it removes the inner T.
To get the desired behavior, you'd force the compiler to default construct the first A:
A a;
// A is now a fully constructed object,
// so it can't call constructors again:
a = A(5);
I was researching this to answer another question that was closed as a dupe, so in order to not let the work go to waste I 'm answering this one instead.
A statement of the form A a = A(5) is called copy-initialization of the variable a. The C++11 standard, 8.5/16 states:
The function selected is called with the initializer expression as
its argument; if the function is a constructor, the call initializes a
temporary of the cv-unqualified version of the destination type. The
temporary is a prvalue. The result of the call (which is the temporary
for the constructor case) is then used to direct-initialize, according
to the rules above, the object that is the destination of the
copy-initialization. In certain cases, an implementation is permitted
to eliminate the copying inherent in this direct-initialization by
constructing the intermediate result directly into the object being
initialized; see 12.2, 12.8.
This means that the compiler looks up the appropriate constructor to handle A(5), creates a temporary and copies that temporary into a. But under what circumstances can the copy be eliminated?
Let's see what 12.8/31 says:
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. This elision of copy/move operations,
called copy elision, is permitted in the following circumstances
(which may be combined to eliminate multiple copies):
[...]
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
Having all this in mind, here's what happens with the expression A a = A(5):
The compiler sees a declaration with copy-initialization
The A(int) constructor is selected to initialize a temporary object
Because the temporary object is not bound to a reference, and it does have the same type A as the destination type in the copy-initialization expression, the compiler is permitted to directly construct an object into a, eliding the temporary
Here you have copy-initialization of a from temporary A(5). Implementation allowed to skip calling copy constructor here according to C++ Standard 12.2/2.
A a = A(5);
This line is equivalent to
A a(5);
Despite its function-style appearance, the first line simply constructs a with the argument 5. No copying or temporaries are involved. From the C++ standard, section 12.1.11:
A functional notation type conversion (5.2.3) can be used to create new objects of its type. [ Note: The
syntax looks like an explicit call of the constructor. —end note ]

Returning unique_ptr from functions

unique_ptr<T> does not allow copy construction, instead it supports move semantics. Yet, I can return a unique_ptr<T> from a function and assign the returned value to a variable.
#include <iostream>
#include <memory>
using namespace std;
unique_ptr<int> foo()
{
unique_ptr<int> p( new int(10) );
return p; // 1
//return move( p ); // 2
}
int main()
{
unique_ptr<int> p = foo();
cout << *p << endl;
return 0;
}
The code above compiles and works as intended. So how is it that line 1 doesn't invoke the copy constructor and result in compiler errors? If I had to use line 2 instead it'd make sense (using line 2 works as well, but we're not required to do so).
I know C++0x allows this exception to unique_ptr since the return value is a temporary object that will be destroyed as soon as the function exits, thus guaranteeing the uniqueness of the returned pointer. I'm curious about how this is implemented, is it special cased in the compiler or is there some other clause in the language specification that this exploits?
is there some other clause in the language specification that this exploits?
Yes, see 12.8 §34 and §35:
When certain criteria are met, an implementation is allowed to omit the copy/move construction of a class object [...]
This elision of copy/move operations, called copy elision, is permitted [...]
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 [...]
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.
Just wanted to add one more point that returning by value should be the default choice here because a named value in the return statement in the worst case, i.e. without elisions in C++11, C++14 and C++17 is treated as an rvalue. So for example the following function compiles with the -fno-elide-constructors flag
std::unique_ptr<int> get_unique() {
auto ptr = std::unique_ptr<int>{new int{2}}; // <- 1
return ptr; // <- 2, moved into the to be returned unique_ptr
}
...
auto int_uptr = get_unique(); // <- 3
With the flag set on compilation there are two moves (1 and 2) happening in this function and then one move later on (3).
This is in no way specific to std::unique_ptr, but applies to any class that is movable. It's guaranteed by the language rules since you are returning by value. The compiler tries to elide copies, invokes a move constructor if it can't remove copies, calls a copy constructor if it can't move, and fails to compile if it can't copy.
If you had a function that accepts std::unique_ptr as an argument you wouldn't be able to pass p to it. You would have to explicitly invoke move constructor, but in this case you shouldn't use variable p after the call to bar().
void bar(std::unique_ptr<int> p)
{
// ...
}
int main()
{
unique_ptr<int> p = foo();
bar(p); // error, can't implicitly invoke move constructor on lvalue
bar(std::move(p)); // OK but don't use p afterwards
return 0;
}
unique_ptr doesn't have the traditional copy constructor. Instead it has a "move constructor" that uses rvalue references:
unique_ptr::unique_ptr(unique_ptr && src);
An rvalue reference (the double ampersand) will only bind to an rvalue. That's why you get an error when you try to pass an lvalue unique_ptr to a function. On the other hand, a value that is returned from a function is treated as an rvalue, so the move constructor is called automatically.
By the way, this will work correctly:
bar(unique_ptr<int>(new int(44));
The temporary unique_ptr here is an rvalue.
I think it's perfectly explained in item 25 of Scott Meyers' Effective Modern C++. Here's an excerpt:
The part of the Standard blessing the RVO goes on to say that if the conditions for the RVO are met, but compilers choose not to perform copy elision, the object being returned must be treated as an rvalue. In effect, the Standard requires that when the RVO is permitted, either copy elision takes place or std::move is implicitly applied to local objects being returned.
Here, RVO refers to return value optimization, and if the conditions for the RVO are met means returning the local object declared inside the function that you would expect to do the RVO, which is also nicely explained in item 25 of his book by referring to the standard (here the local object includes the temporary objects created by the return statement). The biggest take away from the excerpt is either copy elision takes place or std::move is implicitly applied to local objects being returned. Scott mentions in item 25 that std::move is implicitly applied when the compiler choose not to elide the copy and the programmer should not explicitly do so.
In your case, the code is clearly a candidate for RVO as it returns the local object p and the type of p is the same as the return type, which results in copy elision. And if the compiler chooses not to elide the copy, for whatever reason, std::move would've kicked in to line 1.
One thing that i didn't see in other answers is To clarify another answers that there is a difference between returning std::unique_ptr that has been created within a function, and one that has been given to that function.
The example could be like this:
class Test
{int i;};
std::unique_ptr<Test> foo1()
{
std::unique_ptr<Test> res(new Test);
return res;
}
std::unique_ptr<Test> foo2(std::unique_ptr<Test>&& t)
{
// return t; // this will produce an error!
return std::move(t);
}
//...
auto test1=foo1();
auto test2=foo2(std::unique_ptr<Test>(new Test));
I would like to mention one case where you must use std::move() otherwise it will give an error.
Case: If the return type of the function differs from the type of the local variable.
class Base { ... };
class Derived : public Base { ... };
...
std::unique_ptr<Base> Foo() {
std::unique_ptr<Derived> derived(new Derived());
return std::move(derived); //std::move() must
}
Reference: https://www.chromium.org/developers/smart-pointer-guidelines
I know it's an old question, but I think an important and clear reference is missing here.
From https://en.cppreference.com/w/cpp/language/copy_elision :
(Since C++11) In a return statement or a throw-expression, if the compiler cannot perform copy elision but the conditions for copy elision are met or would be met, except that the source is a function parameter, the compiler will attempt to use the move constructor even if the object is designated by an lvalue; see return statement for details.

Why copy constructor is not called in this case?

Here is the little code snippet:
class A
{
public:
A(int value) : value_(value)
{
cout <<"Regular constructor" <<endl;
}
A(const A& other) : value_(other.value_)
{
cout <<"Copy constructor" <<endl;
}
private:
int value_;
};
int main()
{
A a = A(5);
}
I assumed that output would be "Regular Constructor" (for RHS) followed by "Copy constructor" for LHS. So I avoided this style and always declared variable of class as A a(5);. But to my surprise in the code above copy constructor is never called (Visual C++ 2008)
Does anybody know if this behavior is a result of compiler optimization, or some documented (and portable) feature of C++? Thanks.
From another comment: "So by default I should not rely on it (as it may depend on the compiler)"
No, it does not depend on the compiler, practically anyway. Any compiler worth a grain of sand won't waste time constructing an A, then copying it over.
In the standard it explicitly says that it is completely acceptable for T = x; to be equivalent to saying T(x);. (§12.8.15, pg. 211) Doing this with T(T(x)) is obviously redundant, so it removes the inner T.
To get the desired behavior, you'd force the compiler to default construct the first A:
A a;
// A is now a fully constructed object,
// so it can't call constructors again:
a = A(5);
I was researching this to answer another question that was closed as a dupe, so in order to not let the work go to waste I 'm answering this one instead.
A statement of the form A a = A(5) is called copy-initialization of the variable a. The C++11 standard, 8.5/16 states:
The function selected is called with the initializer expression as
its argument; if the function is a constructor, the call initializes a
temporary of the cv-unqualified version of the destination type. The
temporary is a prvalue. The result of the call (which is the temporary
for the constructor case) is then used to direct-initialize, according
to the rules above, the object that is the destination of the
copy-initialization. In certain cases, an implementation is permitted
to eliminate the copying inherent in this direct-initialization by
constructing the intermediate result directly into the object being
initialized; see 12.2, 12.8.
This means that the compiler looks up the appropriate constructor to handle A(5), creates a temporary and copies that temporary into a. But under what circumstances can the copy be eliminated?
Let's see what 12.8/31 says:
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. This elision of copy/move operations,
called copy elision, is permitted in the following circumstances
(which may be combined to eliminate multiple copies):
[...]
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
Having all this in mind, here's what happens with the expression A a = A(5):
The compiler sees a declaration with copy-initialization
The A(int) constructor is selected to initialize a temporary object
Because the temporary object is not bound to a reference, and it does have the same type A as the destination type in the copy-initialization expression, the compiler is permitted to directly construct an object into a, eliding the temporary
Here you have copy-initialization of a from temporary A(5). Implementation allowed to skip calling copy constructor here according to C++ Standard 12.2/2.
A a = A(5);
This line is equivalent to
A a(5);
Despite its function-style appearance, the first line simply constructs a with the argument 5. No copying or temporaries are involved. From the C++ standard, section 12.1.11:
A functional notation type conversion (5.2.3) can be used to create new objects of its type. [ Note: The
syntax looks like an explicit call of the constructor. —end note ]