Is C++17 copy elision applied to placement new? - c++

I am writing a generic interface for scripting languages and need to call functions that return an object and put the resulting object in a space provided by the scripting language.
I have tested with clang++ on Mac OS and the following seems to do the copy/move elision:
class T {
public:
T() {}
T(const T &t) {
std::cout << "Copy" << std::endl;
}
T(T&&t) : x(t.x) {
std::cout << "Move" << std::endl;
}
private:
int x = 3;
};
T h()
{
return T();
}
void buildTInto(void *p)
{
new (p) T(h());
}
int main(int argc, char *argv[])
{
alignas(T) char space[sizeof(T)];
buildTInto(space);
}
Running this code does not print anything.
Reading the cpp reference description of copy elision seems to indicate this is the correct behavior, given the example they give which only calls one constructor:
T x = T(T(f())); // only one call to default constructor of T, to initialize x
I think my code is very much similar as without copy elision, the move constructor would be called (just like the T(T(f())) would call two move construction).
Not being a spec lawyer, I am curious if my understanding is correct and I wonder if all C++17 capable compilers do this correctly?

Guaranteed elision (aka: C++17's definition of prvalues) applies to the initialization of any object of type T with a prvalue of the same type. That includes objects created by new expressions, including placement new forms.
Indeed, you don't even have to repeat the type at the point of invoking the new expression: new(p) auto(h()); ensures the obligate condition for guaranteed elision.

Related

return const value prevent move semantics

I am a beginner in cpp so excuse me for this question.
I was reading that returning const val prevents move semantics.
therefore I dont understand why the following code is compiled and works normally. is it because only temporary object is being created? in what cases the move semantics cannot being done? thank you in advance!
#include <iostream>
using namespace std;
const string foo()
{
return string("hello");
}
int main()
{
string other = std::move(foo());
}
std::move is just a unconditional cast to rvalue. In your case the return value of std::move(foo()) was const std::string&&. And because move constructor does not take const argument, copy constructor was called instead.
struct C {
C() { std::cout << "constructor" << std::endl; }
C(const C& other) { std::cout << "copy constructor" << std::endl; }
C(C&& other) { std::cout << "move constructor" << std::endl; }
};
const C get() {
return C();
}
int main() {
C c(std::move(get()));
return 0;
}
I was reading that returning const val prevents move semantics. therefore I dont understand why the following code is compiled and works normally.
When move semantics are prevented by some mechanism, this doesn't necessarily mean that the code doesn't compile. Often, it compiles happily, but an expected move construction turns out to be a copy instead.
Example: a type has a user provided copy ctor, which disables compiler-generated move ctors. When we think we move-construct, we don't.
struct Test {
Test() = default;
Test(const Test&) {}
};
Test t1;
Test t2{std::move(t1)}; // Copies!
in what cases the move semantics cannot being done?
Coming to your example, something that is const-qualified can't be used to move-construct another object in any meaningful way. Move construction makes sense when resources can be easily transferred, but const-ness prevents that. Example: a type has compiler-generate move and copy constructors, but we can't move-construct from a const instance.
struct Test {
Test() = default;
};
const Test t1;
Test t2{std::move(t1)}; // Copies!
Besides, it doesn't make sense to move something that is returned by a function by value:
string other = std::move(foo());
When foo() returns by value, you can move-construct from it, unless the return type is const. Hence, to enable move-construction of other:
std::string foo();
string other = foo();
std::move doesn't actually move anything. It is just an "rvalue cast". You cast something to rvalue, and the move constructor / move assignment operator does the actual moving if possible. "If possible" part is the key. In your example the return value is already an rvalue, so std::move literally does nothing. You may even get warnings like "nothing is moved". That is because the move constructor of std::string takes an argument of type std::string&& not const std::string&&. Because of that, the copy constructor is called.

Why is using the move constructor in a return statement legal?

Consider the following:
#include <iostream>
#define trace(name) std::cout << #name << " (" << this << "), i = " << i << std::endl
class C
{
C(C const&);
C& operator=(C const&);
public:
int i;
C() : i(42) { trace(CTOR); }
C(C&& other) : i(other.i) { trace(MOVE); other.i = 0; }
C& operator=(C&& other) { trace(ASGN); other.i = 0; return *this; }
~C() { trace(DTOR); }
};
C
func1(bool c)
{
C local;
if (c)
return local;
else
return C();
}
int
main()
{
C local(func1(true));
return 0;
}
Both MSC and g++ allow the return local, and use the move constructor (as shown by the output) when doing so. While this makes a lot of sense to me, and I think it probably should be the case, I can't find the text in the standard that authorizes it. As far as I can see, the argument to the move constructor must be either a prvalue (which it clearly isn't) or an xvalue; it is in fact an lvalue, which would make the return just as illegal as C other = local; in the function body (which does fail to compile).
When move semantics where added to C++ in C++11, decisions where made about where to automatically do move construction.
The general rule that was followed was that implicit move should occur when copy elision would be legal.
Copy elision is legal when you have an anonymous object that you are copying to another instance. The compiler can legally elide the copy, and treat the two objects as one, with a lifetime equal to the union of the two objects lifetime.
The other time that copy elision is legal is when you return a local variable from a function. This was known as NRVO (named return value optimization). This optimization in C++03 let you declare a return value, and it is constructed directly in the place where the return value goes. When you return retval;, no copy happens.
This is basically impossible to do without inlining when you return multiple different objects at different spots in your function.
However, when move semantics where added, this place was the other spot where implicit move can occur. A return local_var; in a function returning a variable the same type as local_var can be elided, and failing that, you can implicitly move from local_var into the return value.
C++11 12.8/32: When the criteria for elision of a copy operation are met or would be met save for the fact that the source object is a function parameter, 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.
Returning a local automatic variable meets the criteria for elision; treating it as if it were an rvalue selects the move constructor.

is twice calls to copy constructor happen for this c++ code sample?

for method:
Object test(){
Object str("123");
return str;
}
then, I had two methods to call it:
code 1:
const Object &object=test();
code 2:
Object object=test();
which one is better? is twice calls to copy constructor happen in code 2 if without optimize?
other what's the difference?
for code2 I suppose:
Object tmp=test();
Object object=tmp;
for code1 I suppose:
Object tmp=test();
Object &object=tmp;
but the tmp will be deconstructor after the method.so it must add const?
is code 1 right without any issues?
Let's analyse your function:
Object test()
{
Object temp("123");
return temp;
}
Here you're constructing a local variable named temp and returning it from the function. The return type of test() is Object meaning you're returning by value. Returning local variables by value is a good thing because it allows a special optimization technique called Return Value Optimization (RVO) to take place. What happens is that instead of invoking a call to the copy or move constructor, the compiler will elide that call and directly construct the initializer into the address of the caller. In this case, because temp has a name (is an lvalue), we call it N(amed)RVO.
Assuming optimizations take place, no copy or move has been performed yet. This is how you would call the function from main:
int main()
{
Object obj = test();
}
That first line in main seems to be of particular concern to you because you believe that the temporary will be destroyed by the end of the full expression. I'm assuming it is a cause for concern because you believe obj will not be assigned to a valid object and that initializing it with a reference to const is a way to keep it alive.
You are right about two things:
The temporary will be destroyed at the end of the full expression
Initializing it with a reference to const will extend its life time
But the fact that the temporary will be destroyed is not a cause for concern. Because the initializer is an rvalue, its contents can be moved from.
Object obj = test(); // move is allowed here
Factoring in copy-elision, the compiler will elide the call to the copy or move constructor. Therefore, obj will be initialized "as if" the copy or move constructor was called. So because of these compiler optimizations, we have very little reason to fear multiple copies.
But what if we entertain your other examples? What if instead we had qualified obj as:
Object const& obj = test();
test() returns a prvalue of type Object. This prvalue would normally be destructed at the end of the full expression in which it is contained, but because it is being initialized to a reference to const, its lifetime is extended to that of the reference.
What are the differences between this example and the previous one?:
You cannot modify the state of obj
It inhibits move semantics
The first bullet point is obvious but not the second if you are unfamiliar with move semantics. Because obj is a reference to const, it cannot be moved from and the compiler cannot take advantage of useful optimizations. Assigning reference to const to an rvalue is only helpful in a narrow set of circumstances (as DaBrain has pointed out). It is instead preferable that you exercise value-semantics and create value-typed objects when it makes sense.
Moreover, you don't even need the function test(), you can simply create the object:
Object obj("123");
but if you do need test(), you can take advantage of type deduction and use auto:
auto obj = test();
Your last example deals with an lvalue-reference:
[..] but the tmp will be destructed after the method. So must we add const?
Object &object = tmp;
The destructor of tmp is not called after the method. Taking in to account what I said above, the temporary to which tmp is being initialized will be moved into tmp (or it will be elided). tmp itself doesn't destruct until it goes out of scope. So no, there is no need to use const.
But a reference is good if you want to refer to tmp through some other variable. Otherwise, if you know you will not need tmp afterwards, you can move from it:
Object object = std::move(tmp);
Both your examples are valid - in 1 const reference refers to a temporary object, but lifetime of this object is prolonged till the reference goes out of scope (see http://herbsutter.com/2008/01/01/gotw-88-a-candidate-for-the-most-important-const/). The second example is obviously valid, and most modern compilers will optimize away additional copying (even better if you use C+11 move semantics) so for practical purposes examples are equivalent (though in 2 additionally you can modify the value).
In C++11, std::string has a move constructor / move assignment operator, hence the code:
string str = test();
will (at worst) have one constructor call and one move assignment call.
Even without move semantics, this will (likely) be optimised away by NRVO (return value optimisation).
Don't be afraid of returning by value, basically.
Edit: Just to make it 100% clear what is going on:
#include <iostream>
#include <string>
class object
{
std::string s;
public:
object(const char* c)
: s(c)
{
std::cout << "Constructor\n";
}
~object()
{
std::cout << "Destructor\n";
}
object(const object& rhs)
: s(rhs.s)
{
std::cout << "Copy Constructor\n";
}
object& operator=(const object& rhs)
{
std::cout << "Copy Assignment\n";
s = rhs.s;
return *this;
}
object& operator=(object&& rhs)
{
std::cout << "Move Assignment\n";
s = std::move(rhs.s);
return *this;
}
object(object&& rhs)
: s(std::move(rhs.s))
{
std::cout << "Move Constructor\n";
}
};
object test()
{
object o("123");
return o;
}
int main()
{
object o = test();
//const object& o = test();
}
You can see that there is 1 constructor call and 1 destructor call for each - NRVO kicks in here (as expected) eliding the copy/move.
Code 1 is correct. As I said, the C++ Standard guarantees a temporary to a const reference is valid. It's main usage is polymorphic behavior with refenences:
#include <iostream>
class Base { public: virtual void Do() const { std::cout << "Base"; } };
class Derived : public Base { public: virtual void Do() const { std::cout << "Derived"; } };
Derived Factory() { return Derived(); }
int main(int argc, char **argv)
{
const Base &ref = Factory();
ref.Do();
return 0;
}
This will return "Derived". A famouse example was Andrei Alexandrescu's ScopeGuard but with C++11 it's even simpler yet.

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

What's the difference between explicit and implicit assignment in C++

int value = 5; // this type of assignment is called an explicit assignment
int value(5); // this type of assignment is called an implicit assignment
What is the difference between those, if any, and in what cases do explicit and implicit assignment differ and how?
http://weblogs.asp.net/kennykerr/archive/2004/08/31/Explicit-Constructors.aspx
EDIT: I actually just found this article, which makes the whole thing a lot clearer... and it brings up another question, should you (in general) mark constructors taking a single parameter of a primitive type - numeric/bool/string - as explicit and leave the rest as they are (of course keeping watch for gotchas such as constructors like (int, SomeType = SomeType())?
Neither of these is an assignment of any kind -- they're both initialization. The first uses copy initialization, and the second direct initialization. (FWIW, I'm pretty sure I've never heard the terms "explicit assignment" or "implicit assignment" before).
Edit: (Mostly in response to Nathan's comment):
Here's a corrected version of the code from your comment:
#include <iostream>
struct Foo {
Foo() {
std::cout << "Foo::ctor()" << std::endl;
}
Foo(Foo const& copy) {
std::cout << "Foo::cctor()" << std::endl;
}
Foo& operator=(Foo const& copy) {
std::cout << "foo::assign()" << std::endl;
return *this;
}
};
int main(int, const char**) {
Foo f;
Foo b(f);
Foo x = b;
return 0;
}
The result from running this should be:
Foo::ctor()
Foo::cctor()
Foo::cctor()
If you run it and get an foo::assign(), throw your compiler away and get one that works (oh, and let us know what compiler it is that's so badly broken)!
They differ if a class has a constructor marked 'explicit'. Then, one of these does not work.
Otherwise, no difference.
Only the first one is an assignment. They are both initialization.
Edit: actually, I'm wrong. Neither are assignment.