How many copies happen/object exist in the following, assuming that normal compiler optimizations are enabled:
std::vector<MyClass> v;
v.push_back(MyClass());
If it is not exactly 1 object creation and 0 copying, What can I do (including changes in MyClass) to achieve that, since it seems to me that that is all that should really be necessary?
If the constructor of MyClass has side-effects, then in C++03 the copy is not permitted to be elided. That's because the temporary object that's the source of the copy has been bound to a reference (the parameter of push_back).
If the copy constructor of MyClass has no side-effects then the compiler is permitted to optimize it away under the "as-if" rule. I think the only sensible way to determine whether it actually has done so with "normal optimizations" is to inspect the emitted code. Different people have different ideas what's normal, and a given compiler might be sensitive to the details of MyClass. My guess is that what this amounts to is whether or not the compiler (or linker) inlines everything in sight. If it does then it will probably optimize, if it doesn't then it won't. So even the size of the constructor code might be relevant, never mind what it does.
So I think the main thing you can do is to ensure that both the default and the copy constructor of MyClass have no side-effects and are available to be inlined. If they're not available then of course the compiler will assume that they could have side-effects and will do the copy. If link-time optimization is a normal compiler option for you, then you don't have to do much to make them available. Otherwise, if they're user-defined then do it in the header file that defines MyClass. You might be able to get away with the default constructor having certain kinds of side-effects: if the effects don't depend on the address of the temporary being different from the address of the vector element then "as-if" still applies.
In C++11 you have a move (that likewise must not be elided if it has side-effects), but you can use v.emplace_back() to avoid that. The move would call the move constructor of MyClass if it has one, otherwise the copy constructor, and everything I say above about "as-if" applies to moves. emplace_back() calls the no-args constructor to construct the vector element (or if you pass arguments to emplace_back then whatever constructor matches those args), which I think is exactly what you want.
You mean:
std::vector<MyClass> v;
v.push_back(MyClass());
None. The temporary will cause the move version of push_back to be called. Even the move construction will most likely be elided.
If you have a C++11 compiler, you can use emplace_back to construct the element at the end of the vector, zero copies necessary.
In C++03, you would have a construction and a copy, plus destruction of the temporary.
If your compiler supports C++11 and MyClass defines a move constructor, then you have one construction and a move.
As mentionned by Timbo, you can also use emplace_back to avoid the move, the object being constructed in-place.
Related
Consider the following code (godbolt):
#include <optional>
#include <array>
struct LargeType {
std::array<int, 256> largeContents;
};
LargeType doSomething();
std::optional<LargeType> wrapIntoOptional(){
return std::optional<LargeType> {doSomething()};
}
As you see, there is a function returning a large POD and then a function wrapping it into a std::optional. As visible in godbolt, the compiler creates a memcpy here, so it cannot fully elide moving the object. Why is this?
If I understand it correctly, the C++ language would allow eliding the move due to the as-if rule, as there are no visible side effects to it. So it seems that the compiler really cannot avoid it. But why?
My (probably incorrect) understanding how the compiler could optimize the memcpy out is to hand a reference to the storage inside the optional to doSomething() (as I guess such large objects get passed by hidden reference anyway). The optional itself would already lie on the stack of the caller of wrapIntoOptional due to RVO. As the definition of the constructor of std::optional is in the header, it is available to the compiler, so it should be able to inline it, so it can hand that storage location to doSomething in the first place. So what's wrong about my intuition here?
To clarify: I don't argue that the C++ language requires the compiler to inline this. I just thought it would be a reasonable optimization and given that wrapping things into optionals is a common operation, it would be an optimization that is implemented in modern compilers.
It is impossible to elide any copy/move through a constructor call into storage managed by the constructed object.
The constructor takes the object as a reference. In order to bind the reference to something there must be an object, so the prvalue from doSomething() must be materialized into a temporary object to bind to the reference and the constructor then must copy/move from that temporary into its own storage.
It is impossible to elide through function parameters. That would require knowing the implementation of the function and the way C++ is specified it is possible to compile each function only knowing the declarations of other functions (aside from constant expression evaluation). This would break that or require a new type of annotation in the declaration.
None of this prevents the compiler from optimizing in a way that doesn't affect the observable behavior though. If your compiler is not figuring out that the extra copy can be avoided and has no observable side effects when seeing all relevant function/constructor definitions, then that's something you could complain to your compiler vendor about. The concept of copy elision is about allowing the compiler to optimize away a copy/move even though it would have had observable side effects.
You can add noexcept to elide copy:
https://godbolt.org/z/rrGEfrdzc
Let's say I have a simple function returnString that returns a string by value:
std::string returnString() {
std::string s;
// Use s in such a way to defeat return value mandatory copy-elision
return s;
}
I have another function that wants to heap-allocate the result of this. Easy enough.
void caller() {
std::string* heap_allocated_string = new std::string(returnString());
}
Instead of std::string though, consider an arbitrary type T. I believe that by the language rules, the following two statements are true. Are they?
In C++14, I believe this is not ideal, since for some types if the move ctor is not free or not defined, we might be doing unnecessary work compared to just directly constructing on the heap.
In C++17, this triggers mandatory copy elision, so even if the type did not define a move constructor, no extra copy would be created, and the move constructor would not be called.
In general though, for a generic type, is there a better way to do this, without modifying the called function?
In C++14, I believe this is not ideal, since for some types if the move ctor is not free or not defined, we might be doing unnecessary work compared to just directly constructing on the heap.
Define "not ideal".
If the object has to be constructed via a factory function which returns by value, and you want to heap-allocate the object instead, and you have to do this "without modifying the called function," then that is as good as it's going to get.
Plus, you need not worry about the lack of copy/move in C++14. The reason being that it is (almost) impossible to return a non-copyable, non-moveable object by value in C++14. There is technically a way to do it (through the use of list-initialization syntax in the return statement), but if the function is written as you've stated it, then whatever type it returns must be copyable or moveable.
Furthermore, the new expression on your end doesn't even require named RVO; this part is just eliding a temporary, and there's no reason why a compiler wouldn't be able to optimize that move away.
So basically, there has to be a copy/move constructor for the function to compile, and any copy/move will be optimized away on your end for all practical purposes. So there's nothing to be concerned about.
Copy initialization is when Hello is created in memory and then being used copy constructor to initialize s, right?
std::string s = std::string("Hello")
After C++11 where move semantics is introduced can I say that the code above is as effective (eliminates copy) as in this case:
std::string s("Hello");
EDIT: please don't answer for string. string was just and example of a class. SSO is not the case what I ask. I ask in general.
When you use strings smaller than 20 characters (depending on the implementation), short string optimization kicks in, and everything is copied anyway.
But to answer your question, move semantics is not used in any of your examples anyway. In the first case even if both copy constructor and string(const char*) constructors must be available, copy elision will eliminate the copy.
EDIT:
To address your edit, if you have one initialization and one initialization + move constructor, the former will obviously always be faster. The reason I brought up SSO is because people assumes that move operations are always cheap (or even free), but not necessarily and sometimes they don't even happen at all.
Short answer: performances should be the same if copy elision is performed. Otherwise the latter should probably be faster.
Long answer:
This code
std::string s = std::string("Hello")
should call a move constructor in C++11+ code (it requires an accessible one). Anyway copy elision is allowed in this case, although not mandated (cfr. [class.copy]/p31)
When
certain criteria are met, an implementation is allowed to omit the
copy/move construction
These are concepts that were already present in pre-C++11 though (they applied to copy constructors as well).
As to the performances question:
The standard also describes a few situations where copying can be eliminated even if this would alter the program's behavior, the most common being the return value optimization. Another widely implemented optimization, described in the C++ standard, is when a temporary object of class type is copied to an object of the same type.[1] As a result, copy-initialization is usually equivalent to direct-initialization in terms of performance, but not in semantics; copy-initialization still requires an accessible copy constructor.[2]
Source - Copy elision
If copy elision doesn't take place (e.g. it has been disabled in gcc via -fno-elide-constructors or for whatever reason the compiler won't perform it) then performances will probably not be the same and direct initialization should be faster (in this case for std::string SSO might also take a toll on the move)
Actually, this was the case before C++11 because of copy elision.
Note that std::string is not necessarily cheap to move, as small strings may be held in the object itself rather than being dynamically allocated. This is known as small string optimisation.
For standard copy constructors and assignment operators, I always think about implementing them or deleteing the defaults out of existence, if my class implements a destructor.
For the new move constructor and move operator, what is the right way to think about whether or not an implementation is necessary?
As a first pass of transitioning a system from pre-C++0x, could I just delete the default move constructor and move operator or should I leave them alone?
You don't have to worry about it, in the sense that when you user-declare a destructor (or anything else listed in 12.8/9), that blocks the default move constructor from being generated. So there's not the same risk as there is with copies, that the default is wrong.
So, as the first pass leave them alone. There may be places in your existing code where C++11 move semantics allow a move, whereas C++03 dictates a copy. Your class will continue to be copied, and if that caused no performance problems in C++03 then I can't immediately think of any reason why it would in C++11. If it did cause performance problems in C++03, then you have an opportunity to fix a bug in your code that you never got around to before, but that's an opportunity, not an obligation ;-)
If you later implement move construction and assignment, they will be moved, and in particular you'll want to do this if you think that C++11 clients of your class are less likely to use "swaptimization" to avoid copies, more likely to pass your type by value, etc, than C++03 clients were.
When writing new classes in C++11, you need to consider and implement move under the same criteria that you considered and implemented swap in C++03. A class that can be copied implements the C++11 concept of "movable", (much as a class that can be copied in C++03 can be swapped via the default implementation in std), because "movable" doesn't say what state the source is left in - in particular it's permitted to be unchanged. So a copy is valid as a move, it's just not necessarily an efficient one, and for many classes you'll find that unlike a "good" move or swap, a copy can throw.
You might find that you have to implement move for your classes in cases where you have a destructor (hence no default move constructor), and you also have a data member which is movable but not copyable (hence no default copy constructor either). That's when move becomes important semantically as well as for performance.
With C++11, you very rarely need to provide a destructor or copy semantics, due to the way the library is written. Compiler provided members pretty much always do fine (provided they are implemented correctly: MSVC forces you to implement a lot of move semantics by hand, which is very bothersome).
In case you have to implement a custom destructor, use the following approach:
Implement a move constructor, and an assignment operator taking by value (using copy&swap: note that you cannot use std::swap since it uses the assignment. You have to provide a private swap yourself). Pay attention to exception guarantees (look up std::move_if_noexcept).
If necessary, implement a copy constructor. Otherwise, delete it. Beware that non default copy semantics rarely make sense.
Also, a virtual destructor counts as a custom destructor: provide or delete copy + move semantics when declaring a virtual destructor.
Since they are used as an optimization you should implement them if the optimization is applicable to your class. If you can "steal" the internal resource your class is holding from a temporary object that is about to be destroyed. std::vector is a perfect example, where move constructor only assigns pointers to internal buffer leaving the temporary object empty (effectively stealing the elements).
Is the compiler allowed to eliminate the copy that is required for the by-value capture?
vector<Image> movie1;
apply( [=movie1](){ return movie1.size(); } );
Is there any circumstance that the compiler does not need to copy movie1?
Maybe if the compiler could know, that apply does not actually change movie1?
Or does it help that Lambdas are by default const functors in any case?
Does it help at all that vector has a move constructor and move assign?
If yes, is it required to add these to Image as well, to prevent an expensive copy here?
Is there a difference in the mechanism when and how a copy is required for by-value capture compared to by-value arguments? eg. void operate(vector<Image> movie)?
I'm fairly sure it cannot.
Even if the outer function no longer explicitly uses the variable, moving the variable would change the semantics of destruction.
Having move constructors for Image doesn't help, a vector can move or swap without moving its elements.
If the variable is read-only from this point forward, why not capture by reference? You could even create a const reference and capture that.
If the variable is not read-only, the copy is required. It doesn't matter whether the outer function or the lambda performs the modification, the compiler cannot allow that modification to become visible to the other.
The only difference I see between by-value capture and by-value argument passing is that the capture is named, it cannot be a temporary. So argument passing optimizations applicable to temporaries cannot be used.
There is always the "as-if" rule. As long as it looks as if the rules had been followed, the compiler can do whatever it likes. So for objects where the copy constructor and destructor have no side effects, and where no changes are made to the copy, or the original object isn't accessed afterwards (so no one will notice if we make changes to the object), the compiler could prove that eliminating the copy is legal under the "as-if" rule.
But other than that, no, it can't just eliminate the copy, as #Ben said. The "regular" copy elision rules don't cover this case.