Hello,
so something I've been wondering for a while. How do you generally account for move-semantics/rvalue-references, overall? What I mean is, in order to take advantage of move-semantics, I've generally been doing the following since now:
1) Make all classes have move-ctors, where possibly and advantageous. Since I'm making havy use of STL wrappers, most classes have functioning default-move-ctors already, with the exceptions being classes making use of std::unique_ptr and the latter.
2) When I know that a function will take an object that is expensive to copy, but I know that its only going to be a temporary in all forseably use cases (ie. a local function that is only called for one-time object initialization/serialization), I declarte it as &&, like this:
class MyClass
{
using MyVector = std::vector<ComplexClass>;
void Function(MyVector&& vData)
{
m_vData = std::move(vData);
}
private:
MyVector m_vVector;
}
However, things get a bit more complicated when I cannot foresee how an variable is to be used, or if I know I will call it with temporaries, as well as have to pass in fixed data members that cannot be moved anyways.
So to all you fellow C++11-users, how dou you take care of this? I bascially see 4 options:
1) Do not account for it at all. Just use
void Function(const MyVector& vData)
{
m_vData = vData;
}
like you would've done without C++11 and move semantics, and take the additional copies, memory allocations and deletions. It doesn't matter in most cases (so we're basically in premature optimization land), and/or maybe the compiler can figure this out for himself (which I doubt in any case where the compiler will not inline the function, but I'm by far no expert).
2) Write both version for move and no-move operations:
void Function(const MyVector& vData)
{
m_vData = vData;
}
void Function(MyVector&& vData)
{
m_vData = std::move(vData);
}
Obviously this will take care of both use cases, but it will require additional work for every function that benefits for move-semantics, and produces code duplication for non-trivial functions. Also it gets really messy once there are multiple parameters that could have move semantics.
3) Write only the move-semantic version
void Function(MyVector&& vData)
{
m_vData = std::move(vData);
}
and when calling the function with a non-temporary, explicitely create a temporary:
const MyVector vDataFromSomewhere;
object.Function(MyVector(vDataFromSomewhere));
While this is just as efficient for this case than before (the temporary I create will be moved in), it requires additional typing for every non-temporary I pass in (so I now have to specify how I want to pass it in via eigther temporary ctor or std::move for every paramter that there is, ugh).
4) Now the next option was pretty much what sparked the question. I found out that I could do this:
void Function(MyVector vData)
{
m_vData = std::move(vData);
}
MyVector vTemporaryData;
object.Function(std::move(vTemporaryData));
Which will move vTemporaryData to vData, and then vData to m_vData. So this means that for non-temporaries, I do not have to make additional typing, and it should also be equally efficient. For temporaries, it should also work like with MyVector&&, though in both cases there is an additional move-ctor call that would otherwise be evaded (though I imagine the compiler could be able to optimize this out, plus an move-ctor call is really nothing compared to copying a vector of 1000 elements).
________________________________________________________________________
So now I know this is not the most important problem in the world and I probably shouldn't worry about it, but its just something that I found interesting and wanted some opinions/real world stories on. Do you account for move-semantics in your code (if you generally use C++11, of course). If so, do you use any of the four options I presented, or do you use it on per-case basis like I used to do before (or maybe is there something completely different that I didn't see like option 4 for the longest time)?
I find option 4 the best in this regard, though it has something weird and unusual, to now suddently be passing in all the expensive objects per value instead of reference... though I guess the same applied to before I found out that I could savely return stuff like MyVector from functions due to RVO/move semantics.