At this point, writing the copy constructor and assignment operator pair is well-defined; a quick search will lead you to plenty of hits on how to properly code these.
Now that the move constructor has entered the mix, is there a new "best" way?
At this point, writing the copy constructor and assignment operator pair is well-defined; a quick search will lead you to plenty of hits on how to properly code these.
Now that the move constructor has entered the mix, is there a new "best" way?
Preferably, they'll just be = default;, since the member types should be of resource managing types that hide the move details from you, like std::unique_ptr. Only the implementors of those "low level" types should bother with dealing with that.
Remember that you only need to bother with move semantics if you're holding an external (to your object) resource. It's completely useless for "flat" types.
= default is only going to work for you if the combined moved-from state of all sub-objects meets the invariant of the containing type, and that has no inherent relationship to whether the sub-objects are "resource-managing types." Usually, that will be the case when the containing type is default-constructible, but even then maybe not. It's easy to come up with counter-examples.class X { std::vector<int> y = {1,2,3}; }; where the desired invariant is that y always has three elements. You can default-construct X, but its =default move constructor will not uphold the desired invariant.The best way is to let the compiler generate them all. It was also the best approach in C++03 and if you managed to do this your C++03 classes automatically become "move-enabled" when you migrate to C++11.
Most resource management issues can be solved by writing just the non-copy constructors and destructors of single-resource managing classes and then only making composite classes using these, plus smart pointers (e.g std::unique_ptr) and container classes to build richer objects.
Using clang/libc++:
#include <chrono> #include <iostream> #include <vector> #if SLOW_DOWN class MyClass { void Swap(MyClass &other) { std::swap(other.member, member); } public: MyClass() : member() { } MyClass(const MyClass &other) : member(other.member) { } MyClass(MyClass &&other) : member(std::move(other.member)) { } MyClass &operator=(MyClass other) { other.Swap(*this); return *this; } private: int member; }; #else class MyClass { public: MyClass() : member() { } private: int member; }; #endif int main() { typedef std::chrono::high_resolution_clock Clock; typedef std::chrono::duration<float, std::milli> ms; auto t0 = Clock::now(); for (int k = 0; k < 100; ++k) { std::vector<MyClass> v; for (int i = 0; i < 1000000; ++i) v.push_back(MyClass()); } auto t1 = Clock::now(); std::cout << ms(t1-t0).count() << " ms\n"; } $ clang++ -stdlib=libc++ -std=c++11 -O3 -DSLOW_DOWN test.cpp $ a.out 519.736 ms $ a.out 517.036 ms $ a.out 524.443 ms $ clang++ -stdlib=libc++ -std=c++11 -O3 test.cpp $ a.out 463.968 ms $ a.out 458.702 ms $ a.out 464.441 ms This looks like approximately 12% speed difference on this test.
Explanation: One of these definitions has a trivial copy constructor and copy assignment operator. The other doesn't. "Trivial" has a real meaning in C++11. It means the implementation is allowed to use memcpy to copy your class. Or to even copy large arrays of your class. So it is best to make your special members trivial if you can. That means letting the compiler define them. Though you can still declare them with = default if you prefer.
SLOW_DOWN is just a very inefficient version. It's not even about comparison to compiler generated constructors etc... Here is two codes on ideone.com: first one uses OP's version T & operator( T ), second uses explicitly defined T &( T && ) and T & operator=( T && ). Compare the output between -----------...This is what I've come up with, but I don't know if there's a more optimal solution out there.
class MyClass { void Swap(MyClass &other) { std::swap(other.member, member); } public: MyClass() : member() { } MyClass(const MyClass &other) : member(other.member) { } MyClass(MyClass &&other) : member(std::move(other.member)) { } MyClass &operator=(MyClass other) { other.Swap(*this); return *this; } private: int member; }; MyClass really is supposed to have these semantics, this is probably the very worst (poorest performing) way possible to write the special members and yet still have it be considered correct. Sorry, to be so direct, but I thought you should know.MyClass lC = MyClass(), lC1; lC1 = lC;?