Move ctor is not called

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

Answers (5)

Zach Saw
Zach Saw

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;
}
  • Some compilers do RVO regardless of compiler settings (debug / release mode) - e.g. bcc32. I have a feeling VC would be the same.

Upvotes: 0

Roger Pate
Roger Pate

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

Puppy
Puppy

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

fredoverflow
fredoverflow

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

UncleBens
UncleBens

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

Related Questions