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.
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.
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;
};