From what I understand, if I wanted to use Move Semantics efficiently, I would need to have a pointer to my vector of children to std::move that pointer effortlessly, am I correct in saying that if my class has no pointers, it is useless to std::swap?
No that's not true in general, the standard library container classes will have move constructors that do what you want as long as you know how to invoke them, which brings us to the second part of your question:
Does std::swap do this in my case?
I know it can be really frustrating to get the answer "it depends", but as with most things in C++, it really depends.
To answer your question in the most general way possible, yes std::swap()
will probably do what you want most of the time, especially if you're just working with standard library container classes. Where things get weird (and where I don't have enough information to give you a complete answer) is that you've defined your own class, and only part of it is shown here. The devil is in the details, so the actual behavior of the program will depend on what's in those ellipses.
In general when you're trying to understand what to expect with copy/move behavior, you really need to think in terms of constructors. Assuming you're using a "modern" (i.e. post-11 version) of C++, the std::swap()
function is going to look something roughly like this:
template<typename T> void swap(T& t1, T& t2) {
T temp = std::move(t1); // or T temp(std::move(t1));
t1 = std::move(t2);
t2 = std::move(temp);
}
See also this related post. More concretely for your example, the template instantiation will look something like this:
void swap(JSON& t1, JSON& t2) {
JSON temp = std::move(t1); // or T temp(std::move(t1));
t1 = std::move(t2);
t2 = std::move(temp);
}
Keep in mind that std::move()
is really just a fancy way to cast an lvalue reference to an rvalue reference with some corner case handling. The function itself doesn't do anything, it's a means to tell the compiler how to perform overload resolution.
So now the question becomes: what happens when the compiler needs to construct a JSON object from an rvalue reference to an object type JSON
? The answer to this question depends on what constructors are available on the class, some of which may be implicitly generated by the compiler. See also this post.
The compiler will pick the best fitting constructor for the operation, which could be an implicit one, and depending on what you've declared on class, may not actually be a move constructor as explained in this example. To stay away from falling into that trap, you need to know that an rvalue reference can bind to a const
lvalue reference, so a copy constructor with the following signature:
JSON(const JSON &);
Is a valid overload candidate for the left hand side of std::move()
operation in some cases. This is probably why you sometimes hear people saying that std::move()
"isn't actually moving anything", or it's "still just copying".
So where does all of this leave your code? Basically if you have no user-declared constructors, and you're letting the compiler do it for you, then std::swap
is probably going to move memory on all of your members the way you want. As soon as you start declaring your own constructors, the story gets more complicated and we have to talk specifics.
As a small postscript here, do you really need to use swap()
at all? It looks like you're just trying to construct a shared_ptr
to an object that's been initialized with the contents of another object. This would probably be a slightly simpler approach:
std::shared_ptr<const JSON> outPtr = std::make_shared<JSON>(std::move(json["data"]));
This will construct an object of type JSON using a move constructor (assuming it's the best overload candidate given the caveats I mentioned) and return a shared_ptr
to it.