Reputation: 711
I'm trying to learn a little more about c++ and I'm a bit confused by what my compiler is doing. I have written the following file with comments detailing what happens:
Test getTest()
{
return Test(100, string("testing..."));
}
int main()
{
// These two will call the initializer constructor...
Test t1(5, string("hello"));
Test t2(10, string("goodbye"));
// This will not call operator=. This will call the copy constructor!
Test t3 = t1;
// This will call operator=(Test&)
t3 = t2;
// This will call operator=(Test&&) because rhs is an rvalue
// We will swap the resources in this operator= so that when getTest()
// deletes its resources, it will actually be deleting t3's old resources.
// Likewise, t3 will get getTest()'s resources.
t3 = getTest();
// I don't know what this is doing, but I know it's not calling the destructor.
// I beleive that the memory of t4 is simply what was returned by getTest().
// Likewise with t5.
Test t4(getTest());
Test* t5 = new Test(getTest());
Test t6(t4);
return 0;
}
It appears that t4 and t5 aren't entering any constructors, and in fact are just using the memory allocated by getTest(). What I assumed would happen was that t4 would enter the rValue copy constructor: Test(const Test&& rhs), but it does not even though its argument is an rValue. Test t4(getTest()) does not call any destructors, which is why I assume t4 is just acquiring the memory. t6 does call the copy constructor.
I looked in the assembly code in Visual Studio 2013 and found the following:
Test t4(getTest());
00F59B8C push 8
00F59B8E lea ecx,[t4]
00F59B91 call Test::__autoclassinit2 (0F51285h)
00F59B96 lea eax,[t4]
00F59B99 push eax
00F59B9A call getTest (0F51456h)
00F59B9F add esp,4
00F59BA2 mov byte ptr [ebp-4],8
So it looks like its calling something known as autoclassinit2, then getting the memory from getTest, and finally storing it in t4?
So I guess my question is: Is this just a compiler optimization to directly give the memory from the constructor in getTest() to t4? Instead of say, 1. constructing in getTest() 2. Calling rVal copy constructor 3. destroying getTest() memory? Or is something else going on here? Thanks!
Upvotes: 3
Views: 1146
Reputation: 275800
=
in a declaration means 'implicit construction please' (basically) and not 'call operator='.
C++ has a concept known as elision. Elision means 'make two or more variables the same thing'. There are rules about when the compiler can do it. Unlike other optimizations, it is legal even if there are side effects.
If you initialize a variable with an unnamed temporary of the same type, the compiler can elide the temporary. If you return a local variable from a function, it can (in certain circumstances) be elided into the unnamed return value. Ditto for unnamed return of temporaries. NRVO and RVO are the keywords you want.
In many cases, if elision fails for a technical reason or compiler limitation, move
implicitly occurs. However explicit move
blocks elision. So knowing the rules is important for bleeding edge optimal code.
Upvotes: 4