Recently I read an example from cppreference.../vector/emplace_back:
struct President
{
std::string name;
std::string country;
int year;
President(std::string p_name, std::string p_country, int p_year)
: name(std::move(p_name)), country(std::move(p_country)), year(p_year)
{
std::cout << "I am being constructed.\n";
}
My question: is this std::move
really needed? My point is that this p_name
is not used in the body of constructor, so, maybe, there is some rule in the language to use move semantics for it by default?
That would be really annoying to add std::move on initialization list to every heavy member (like std::string
, std::vector
). Imagine hundreds of KLOC project written in C++03 - shall we add everywhere this std::move
?
This question: move-constructor-and-initialization-list answer says:
As a golden rule, whenever you take something by rvalue reference, you need to use it inside std::move, and whenever you take something by universal reference (i.e. deduced templated type with &&), you need to use it inside std::forward
But I am not sure: passing by value is rather not universal reference?
[UPDATE]
To make my question more clear. Can the constructor arguments be treated as XValue - I mean expiring values?
In this example AFAIK we do not use std::move
:
std::string getName()
{
std::string local = "Hello SO!";
return local; // std::move(local) is not needed nor probably correct
}
So, would it be needed here:
void President::setDefaultName()
{
std::string local = "SO";
name = local; // std::move OR not std::move?
}
For me this local variable is expiring variable - so move semantics could be applied... And this similar to arguments passed by value....
In general, when you want to turn an lvalue to an rvalue, then yes, you need a
std::move()
. See also Do C++11 compilers turn local variables into rvalues when they can during code optimization?Here, I would want the optimizer to eliminate the superfluous
local
ALTOGETHER; unfortunately, it is not the case in practice. Compiler optimizations get tricky when heap memory comes in to play, see BoostCon 2013 Keynote: Chandler Carruth: Optimizing the Emergent Structures of C++. One of my takeaways from Chandler's talk is that optimizers simply tend to give up when it comes to heap allocated memory.See the code below for a disappointing example. I don't use
std::string
in this example because that's a heavily optimized class with inline assembly code, often yielding counterintuitive generated code. To add injury to insult,std::string
is roughly speaking a reference counted shared pointer in gcc 4.7.2 at least (copy-on-write optimization, now forbidden by the 2011 standard forstd::string
). So the example code withoutstd::string
:Clearly, according to the "as-if" rule, the generated code could be optimized to this:
I have tried it with GCC 4.9.0 and Clang 3.5 with link time optimizations enabled (LTO), and none of them could optimize the code to this level. I looked at the generated assembly code: They both allocated the memory on the heap and did the copy. Well, yeah, that's disappointing.
Stack allocated memory is different though:
I have checked the assembly code: Here, the compiler is able to reduce the code to basically just
std::printf("string literal\n");
.So my expectations that the superfluous
local
in your example code could be eliminated is not completely unsupported: As my latter example with the stack allocated array shows, it can be done."Want speed? Measure." (by Howard Hinnant)
You can easily find yourself in a situation that you do your optimizations just to find out that your optimizations made the code slower. :( My advice is the same as Howard Hinnant's: Measure.
Yes, but we have rules for this special case: It is called named return value optimization (NRVO).
Of course it's needed.
p_name
is a lvalue, hencestd::move
is needed to turn it into a rvalue and select the move constructor.That's not only what the language says -- what if the type is like this:
The language mandates that
copy ctor
must be printed if you omit the move. There are no options here. The compiler can't do this any different.Yes, copy elision still applies. But not in your case (initialization list), see the comments.
Or does your question involve why are we using that pattern?
The answer is that it provides a safe pattern when we want to store a copy of the passed argument, while benefiting from moves, and avoiding a combinatorial explosion of the arguments.
Consider this class which holds two strings (i.e. two "heavy" objects to copy).
So let's see what happens in various scenarios.
Take 1
Argh, this is bad. 4 copies happening here, when only 2 are really needed. In C++03 we'd immediately turn the Foo() arguments into const-refs.
Take 2
Now we have
That's much better!
But what about moves? For instance, from temporaries:
Or when explicitely moving lvalues into the ctor:
That's not that good. We could've used
string
's move ctor to initialize directly theFoo
members.Take 3
So, we could introduce some overloads for Foo's constructor:
So, ok, now we have
Good. But heck, we get a combinatorial explosion: each and every argument now must appear in its const-ref + rvalue variants. What if we get 4 strings? Are we going to write 16 ctors?
Take 4 (the good one)
Let's instead take a look at:
With this version:
Since moves are extremely cheap, this pattern allows to fully benefit from them and avoid the combinatorial explosion. We can indeed move all the way down.
And I didn't even scratch the surface of exception safety.
As part of a more general discussion, let's now consider the following snippet, where all the classes involved make a copy of s by pass by value:
If I got you correctly, you'd really like that the compiler actually moved
s
away on its last usage:I told you why the compiler cannot do that, but I get your point. It would make sense from a certain point of view -- it's nonsense to make
s
live any longer than its last usage. Food for thought for C++2x, I guess.The current rule, as amended by DR1579, is that xvalue transformation occurs when a NRVOable local or parameter, or an id-expression referring to a local variable or parameter, is the argument to a
return
statement.This works because, clearly, after the
return
statement the variable can't be used again.Except that's not the case:
So even for a
return
statement, it's not actually guaranteed that a local variable or parameter appearing in that statement is the last use of that variable.It's not totally out of the question that the standard could be changed to specify that the "last" usage of a local variable is subject to xvalue transformation; the problem is defining what the "last" usage is. And another problem is that this has non-local effects within a function; adding e.g. a debugging statement lower down could mean that an xvalue transformation you were relying on no longer occurs. Even a single-occurrence rule wouldn't work, as a single statement can be executed multiple times.
Perhaps you'd be interested in submitting a proposal for discussion on the std-proposals mailing list?
I made some further investigation and querying another forums on net.
Unfortunately it seems that this
std::move
is necessary not only because C++ standard says so, but also otherwise it would be dangerous:((credit to Kalle Olavi Niemitalo from comp.std.c++ - his answer here))