Reputation: 24675
Am I doing something wrong (again)?
#include <iostream>
using std::cout;
struct Map
{
Map()
{
cout << "Map()\n";
}
Map(const Map& pattern)
{
cout << "Map(const Map& pattern)\n";
}
Map(Map&& tmp)
{
cout << "Map(Map&& tmp)\n";
}
};
Map createMap()
{
return Map();
}
int main(int argc, char* argv[])
{
//dflt
Map m;
//cpy
Map m1(m);
//move
Map m2(Map(m1));//<<I thought that I create here tmp unnamed obj.
Map m3(createMap());//<<---or at least here, but nope...
return 0;
}
Please see the commented line in the code
Edited [taken from FredOverflow answer]
int main()
{
std::cout << "default\n";
Map m;
std::cout << "\ncopy\n";
Map m1(m);
std::cout << "\nmove\n";
Map m2((Map(m1)));
std::cout << "\nmove\n";
Map m3(createMap());
}
I'm getting output:
default
Map()
copy
Map(const Map& pattern)
move
Map(const Map& pattern)//Here why not move ctor aswell as copy?
move
Map()
Map()
Map(Map&& tmp)
Upvotes: 0
Views: 292
Reputation: 4378
Map createMap()
{
return Map();
}
I would think that the compiler would've done an RVO (return value optimization) on the above, thus no temporaries would ever be created.
If you change it to the following, you should see your move ctor getting invoked.
Map createMap()
{
Map m;
m.DoSomething(); // this should make the compiler stop doing RVO
return m;
}
Upvotes: 0
Reputation:
You're declaring a function, not an object:
T name (T(blah));
Is equivalent to:
T name(T blah);
Which is recognizable as a function declaration. You can use extra parens:
Map m2 ((Map(m1)));
This is called the most vexing parse.
Upvotes: 3
Reputation: 146940
This program shows expected output.
#include <iostream>
using std::cout;
struct Map
{
Map()
{
cout << "Map()\n";
}
Map(const Map& pattern)
{
cout << "Map(const Map&)\n";
}
Map(Map&& tmp)
{
cout << "Map(Map&&)\n";
}
};
Map createMap()
{
Map m;
return m;
}
int main(int argc, char* argv[])
{
//dflt
Map m;
//cpy
Map m1(m);
//move
Map m2 = createMap();//<<I thought that I create here tmp unnamed obj.
std::cin.get();
return 0;
}
Note the changes to createMap(). It doesn't employ a direct temporary but a named return value. This program shows the intended output on Visual Studio 2010.
Upvotes: 1
Reputation: 263138
I slightly modified your main
routine to understand the output better:
int main()
{
std::cout << "default\n";
Map m;
std::cout << "\ncopy\n";
Map m1(m);
std::cout << "\nmove\n";
Map m2(Map(m1));
std::cout << "\nmove\n";
Map m3(createMap());
}
And here is the output with g++ -fno-elide-constructors
:
default
Map()
copy
Map(const Map& pattern)
move
move
Map()
Map(Map&& tmp)
Map(Map&& tmp)
As others already pointed out, Map m2(Map(m1));
is indeed a function declaration, so you get no output. The second move is not interpreted as a function declaration, because createMap
is not a type name. There are two move constructors involved here. One moves the temporary object created by evaluating Map()
into the temporary object created by evaluating createMap()
, and the second move initializes m3
from the latter. This is exactly what one would expect.
If you fix the first move by writing Map m2((Map(m1)));
the output becomes:
move
Map(const Map& pattern)
Map(Map&& tmp)
Again, no surprises. The copy constructor is invoked by evaluating Map(m1)
, and that temporary object is then moved into m2
. If you compile without -fno-elide-constructors
, the move operations disappear, because they are replaced by even more efficient optimizations like RVO or NRVO:
default
Map()
copy
Map(const Map& pattern)
move
Map(const Map& pattern)
move
Map()
I'm sure Visual C++ has a compiler option similar to -fno-elide-constructors
that you can play with to understand move semantics better.
Upvotes: 1
Reputation: 41331
Map m3(createMap());//<<---or at least here, but nope...
You are seeing return value optimization in action. In C++, the compiler is allowed to optimize away copying returned objects, and let the function work directly with the caller's object where the result is stored. There isn't any need to invoke the move constructor either.
Make the function more complicated, so that the compiler cannot use the optimization, and you'll see moving in action. For example:
Map createMap()
{
Map a, b;
if (rand())
return a;
return b;
}
Upvotes: 4