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....
std::move itself does "nothing" - it has zero side effects. It just signals to the compiler that the programmer doesn't care what happens to that object any more. i.e. it gives permission to other parts of the software to move from the object, but it doesn't require that it be moved.
Initialization lists. In fact, constructors should initialize as a rule all member objects in the initialization list.
Initializer List is used in initializing the data members of a class. The list of members to be initialized is indicated with constructor as a comma-separated list followed by a colon. Following is an example that uses the initializer list to initialize x and y of Point class.
Conclusion: All other things being equal, your code will run faster if you use initialization lists rather than assignment.
My question: is this std::move really needed? My point is that compiler sees that this p_name is not used in the body of constructor, so, maybe, there is some rule to use move semantics for it by default?
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?
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....
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 for std::string
). So the example code without std::string
:
#include <algorithm> #include <cstdio> int main() { char literal[] = { "string literal" }; int len = sizeof literal; char* buffer = new char[len]; std::copy(literal, literal+len, buffer); std::printf("%s\n", buffer); delete[] buffer; }
Clearly, according to the "as-if" rule, the generated code could be optimized to this:
int main() { std::printf("string literal\n"); }
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:
#include <algorithm> #include <cstdio> int main() { char literal[] = { "string literal" }; const int len = sizeof literal; char buffer[len]; std::copy(literal, literal+len, buffer); std::printf("%s\n", buffer); }
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.
Imagine hundreds of KLOC project written in C++03 - shall we add everywhere this
std::move
?
[...]
But I am not sure: passing by value is rather not universal reference?
"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.
std::string getName() { std::string local = "Hello SO!"; return local; // std::move(local) is not needed nor probably correct }
Yes, but we have rules for this special case: It is called named return value optimization (NRVO).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With