views:

113

answers:

7

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)
A: 

You are not invoking the contructor in this case but IMO you are invoking the copy constructor.

Als
@Als but the last one should invoke move ctor. There is unnamed tmp object pass as a argument isn't it?
There is nothing we can do
A: 

Try adding new.

BoomyJee
+3  A: 

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.

Roger Pate
@Roger and what about the last line? Clearly there is a fnc invocation, so it cannot be a declaration of another fnc.
There is nothing we can do
@Roger None of your examples seems to work for me. In VS2010 having this code doesn't invoke move ctor and I think is should (the line where createMap() is called at least.)
There is nothing we can do
@Roger and most of all your example with auto Map ... doesn't compile (in VS2010)
There is nothing we can do
I could've sworn auto was usable like that, but I must be thinking of something else.
Roger Pate
`createMap` does not name a type, right? Are you sure `Map m3(createMap());` is really a function declaration then?
FredOverflow
@Fred: You're right, I jumped the gun on that one.
Roger Pate
+4  A: 
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;
}
UncleBens
@UncleBens Thanks for your answer. But why this line (with m2 and invocation of createMap) isn't even called? The object m2 isn't created at all.
There is nothing we can do
@UncleBens yes thanks it does work (the complication of createMap). Thanks. +1
There is nothing we can do
+1  A: 

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.

FredOverflow
@Fred could you please check my edited post?
There is nothing we can do
@There: As I already pointed out, most compilers are clever enough to apply even better optimizations, and thus moving is often not necessary.
FredOverflow
+1  A: 

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.

DeadMG
So does VS not do NRVO?
FredOverflow
@FredOverflow: Not in debug mode. Of course, you don't see that output in Release mode.
DeadMG
@DeadMG thanks for your answer +1. So BASICALLY I got the whole move semantics idea right, I just wasn't aware of the optimizatiooon business. – There is nothing we can do 0 secs ago edit
There is nothing we can do