Continuing with the C++11 saga, this week I’ve been playing with move semantics. In this post I share my findings regarding using it on Apple’s Clang version 4.0.
Move semantics are a new mechanism built into C++ that allow us to “move” data from one object to another. They help avoid full object copies that, today, could occur under different scenarios.
The copy involved in passing in values to a function is a good example that can usually be avoided by receiving parameters as const references instead of as values. Other scenarios, like returning an object from a function, are, however, harder to avoid.
Move semantics let us write special functions that can “steal” a source object’s inner data and assign it to a new object. It could help you avoid an expensive copy operation when you know the source object is not going to be used anymore.
I’ve written a simple example to illustrate the problem. Suppose we have class A and we want to have a function that creates an A instance, then customizes it and finally returns it to the caller.
// A's interface:
// Default ctor:
// Copy ctor:
A(const A& other);
// Copy-assignment operator:
A& operator=(const A& other);
// Inner data, should be private...
// A's implementation:
A::A() : _a(0.0f)
cout << "Running A()" << endl;
A::A(const A& other)
cout << "Running A(const A&)" << endl;
_a = other._a;
A& A::operator=(const A& other)
cout << "Running operator=(const A&)" << endl;
_a = other._a;
// Helper function to create and customize an A instance:
a._a = 1.0f;
// Program entry point:
int main(int argc, char* argv)
cout << "==Declaring A a==" << endl;
cout << "==Assigning a=getAnA()==" << endl;
a = getAnA();
cout << "==done==" << endl;
cout << "a._a = "<< a._a << endl;
Running this code yields the following output:
==Declaring A a==
Running operator=(const A&)
a._a = 1
The problem here lays in line 6. When the instance is to be returned from function getAnA(), it lives in the called function’s stack, so we need to copy it into the caller’s stack space in orde to return it. The code that does the copy is generated automatically by the C++ compiler and will incur in a performance penalty.
How slow is it? Well, it depends. For a big object (for instance a memory page, a huge matrix or other gigantic data structure) the performance penalty can be significant, specially when we really start to pass objects around.
This copy, however could completely be avoided by leveraging the fact that the source instance is going to go away. Move semantics allow us to have the destination A steal (or move) the source A’s inner data.
Next week I’ll show you how you can implement move semantics to speed up object passing. Stay tuned!.