Using std::move inside a move constructor - c++11

This answer writes a move constructor like this:
dumb_array(dumb_array&& other)
: dumb_array() // initialize via default constructor, C++11 only
{
swap(*this, other);
}
Note: this swap is a custom swap also defined on the class, not std::swap.
This answer suggests that use of std::move:
you should only call std::move where appropriate, but this is were personal preferences come in.
I just watched a Scott Meyers talk on move semantics where he stated that an rvalue reference as a function parameter is turned back into an lvalue by virtue of the fact that it is now named, and therefore std::move is necessary once again turn it back into an rvalue.
In other words, the code above may very well not be doing a move operation, and that, unlike the quote above, use of std::move in such contexts is not about personal preference but about actually gaining benefits of move semantics.
Is it accurate to say then that if std::move is not explicitly used inside a move constructor or a move assignment operator, moving is probably not actually happening, and instead a copy is being done?

Is it accurate to say then that if std::move is not explicitly used inside a move constructor or a move assignment operator, moving is probably not actually happening, and instead a copy is being done?
No, that is most certainly not accurate. In the particular case in the question, it fully depends on the implementation of swap.
A natural implementation would be swap(dumb_array&, dumb_array&) which internally swaps some pointers, perhaps like this:
void swap(dumb_array &lhs, dumb_array &rhs)
{
std::swap(lhs.pointer_to_data, rhs.pointer_to_data);
}
If that is the case, then the move constructor you've shown certainly performs a lightweight move—just a pointer swap. Second, using std::move when calling swap would be downright wrong, as swap takes lvalue references.

I just watched a Scott Meyers talk on move semantics where he stated that an rvalue reference as a function parameter is turned back into an lvalue by virtue of the fact that it is now named, and therefore std::move is necessary once again turn it back into an rvalue.
That's correct.
But there are many misconceptions around this topic.
In other words, the code above may very well not be doing a move operation
It won't do a move. However, it's likely that swap is an efficient operation anyway and therefore you don't care.
use of std::move in such contexts is [...] about actually gaining benefits of move semantics.
Not true. Even if you use std::move, there is no guarantee that any moving will occur.
Is it accurate to say then that if std::move is not explicitly used inside a move constructor or a move assignment operator, moving is probably not actually happening, and instead a copy is being done?
moving can occur, even if there is no std::move.
So basically, std::move is neither necessary, nor sufficient, for moving to occur. This may seem quite surprising.
I think there is really no shortcut to this. If you want to understand this, find a good comprehensive tutorial. E.g. The popular C++ Rvalue References Explained by Thomas Becker.

Related

Is a unique_ptr equipped with a function pointer as custom deleter the same size as a shared_ptr?

I know that std::unique_ptr and std::shared_ptr are different classes that address different needs, and therefore asking which one is better is mostly an ill-posed question.
However, as regards their content and performance, without considering the different semantics of the two smart pointers, I have some doubt I want clarify.
My understanding is that a std::unique_ptr contains the raw pointer as its only member variable, and stores the deleter, if any custom one is given, as part of the type; whereas the std::shared_ptr stores in member variables the raw as well as the pointer to a dynamically allocated block which contains the custom deleter, and strong and weak counters.
Scott Meyers, in Effective Modern C++, stresses a lot on this difference in the size that the two smart pointers require (and on the difference in performance in general), however I'd be tempted to say that as soon as a std::unique_ptr is provided with a function pointer as custom deleter it becomes as big as std::shared_ptr, whose size does not increase with the custom deleter.
From there, I would deduce that using a function pointer as a custom deleter for std::unique_ptr basically annihilates the advantage this smart pointer has on the other one, in terms of size/performance.
Is this the case?
It is true (I doubt that this is required by the standard though) that
static_assert(sizeof(std::unique_ptr<int, void(*)(int*)>) == sizeof(std::shared_ptr<int>));
but I don't agree with the conclusion that storing a function pointer as the custom deleter renders the advantages of std::unique_ptr over std::shared_ptr useless. Both types model very different ownership semantics, and choosing one over the other has not so much to do with performance, but rather with how you intend to handle the pointee instances.
Performance-wise, std::unique_ptr will always be more efficient than std::shared_ptr. While this is primarily due to thread-safe reference counting of the latter, it is also true for custom deleters:
std::unique_ptr stores the deleter in-place, i.e., on the stack. Invoking this deleter is likely to be faster than one that lives in a heap-allocated block together with pointee and reference count.
std::unique_ptr also doesn't type-erase the deleter. A lambda that is baked into the type is certainly more efficient than the indirection required to hide the deleter type as in std::shared_ptr.

C++ primer 5 edition: Container of shared_ptr

Again reading C++ Primer 5 Edition. I am on chapter 12 Dynamic memory. Everything is OK. Until this point in the book:
"Because memory is not freed until the last shared_ptr goes away, it can be important to be sure that shared_ptrs don’t stay around after they are no longer needed.The program will execute correctly but may waste memory if you neglect to destroy shared_ptrs that the program does not need.One way that shared_ptrs might stay around after you need them is if you put shared_ptrs in a container and subsequently reorder the container so that you don’t need all the elements.You should be sure to erase shared_ptr elements once you no longer need those elements.
Note
If you put shared_ptrs in a container, and you subsequently need to use some, but not all, of the elements, remember to erase the elements you no longer need."
I don't understand this paragraph can someone explain to me how may shared_ptrs leaks? and an example of the "container" of shared_ptr that can cause leak. Thank you.
It essentially means that as long as you have a std::shared_ptr object in your container the object it points to will not be deleted.
So once you have no more use of that object, you should remove the corresponding std::shared_ptr from your container so the storage can be freed.
If you were to keep adding elements to your container and never removing any, you would essentially leak memory (ofc it will be cleaned up when the reference count hits 0, but until then it's reserved for no reason).
Side note, make sure you always think about when you are using std::shared_ptr. Often a std::unique_ptr is enough and should the need arise to make it shared it's easy to do that.
See Does C++11 unique_ptr and shared_ptr able to convert to each other's type?
also
https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rr-unique

Any documentation/article about the `&MyType{}` pattern in golang?

In most golang codebases I look, people are using types by reference:
type Foo struct {}
myFoo := &Foo{}
I usually take the opposite approach, passing everything as copy and only pass by reference when I want to perform something destructive on the value, which allows me to easily spot destructive functions (and which is fairly rare).
But seeing how references are commonplace, I guess it's not just a matter of taste. I get there's a cost in duplicating values, is it that much of a game changer? Or are there other reasons why references are preferred?
It would be great if someone could point me to an article or documentation about why references are preferred.
Thanks!
Go is pass by value. I try to use references like in your example as much as possible to remove the mental process of thinking about not making duplicates of objects. Go is mostly meant for networking & scaling, which makes performance a priority. Obvious downside of this is as you say, receiving methods can destroy the object that the pointer points to.
Otherwise there is no rule as to which you should use. Both are quite ok.
Also, somewhat related to the question, from the Go docs: Pointers vs. Values

(Idiomatic?) Difference between new(T) and &T{...}?

I started kidding around with Go and am a little irritated by the new function. It seems to be quite limited, especially when considering structures with anonymous fields or inline initialisations. So I read through the spec and stumbled over the following paragraph:
Calling the built-in function new or taking the address of a composite literal allocates storage for a variable at run time.
So I have the suspicion that new(T) and &T{} will behave in the exact same way, is that correct? And if that is correct, in what situation should new be used?
Yes, you are correct. new is not that useful with structs. But it is with other basic types. new(int) will get you a pointer to a zero-valued int, and you can't do &int{} or similar.
In any case, in my experience, you rarely want that, so new is rarely used. You can just declare a plain int and pass around a pointer to it. In fact, doing this is probably better because liberates you from thinking about allocating in the stack vs. in the heap, as the compiler will decide for you.

How to localize places for introducing move semantics in a legacy code base?

We have a pretty huge code base, sometimes with performance issues. Move semantics is not used at all. I am wondering how could I find places where move semantics might be useful. Do you have any experience how to localize such a places?
Since the standard containers have been updated to move elements whenever possible, a good place to start would be types that are stored (by value) in standard containers. If you make them MoveConstructible and MoveAssignable (and be sure to make the move operations noexcept where possible so e.g. std::vector will actually move not copy) then all code using containers of those elements can potentially benefit.
Other candidates are any non-trivial types that are passed to functions or returned from functions by value.
Of course another approach is the usual one for any performance issues: profile the code and find out what's slow. If it's due to lots of copying then consider if the copies can be avoided, and if not whether implementing move semantics might help. If the slowness isn't due to lots of copying then move semantics probably won't help.
Move construction is a great feature because once you add a move constructor you get all the performance benefit "for free". Sure, you can explicitly move objects, but many uses of the move constructor are actually implicit.
Some places (far from an exhaustive list) that benefit once you add a move constructor to a class would be:
std::vector<T> vec;
vec.push_back(T()); //implicit move into array
std::sort(vec.begin(), vec.end()); //may move if std::swap performs swap
T obj = foo(); //move construct if compiler doesn't apply RVO
T obj2 = obj + T(); //technically same as the line above
The great part about it is that once you add a move constructor to the class (usually only a few lines of code) you don't have to change ANY other code to start reaping the benefits of move!
Your biggest gains would most likely be in loops adding elements to containers, but you get an improvement in all of these areas just by adding a move constructor (which is often trivial to write).
However, 99% of the classes that will benefit from move are classes which allocate memory or resources, because move allows you to replace a deep copy with a shallow copy. Most other classes (such as classes that are just record types) don't gain anything from move.

Resources