C++ constructor with rvalue reference
Asked Answered
B

3

13

Consider this class with three constructors:

class Circle {

public:
 Circle(int r) {
      _radius = r;
 }

Circle(const Circle& c){
    _radius = c.radius();
    cout << endl << "Copy constructor with lvalue reference. Radius: " << _radius;
}

Circle(Circle&& c){
    _radius = c.radius();
    cout << endl << "Copy constructor with rvalue reference. Radius:" << _radius;
}

int radius() const {
    return _radius;
}

private:
    int _radius;
};

int main() {
     Circle c1(2);
     Circle c2(c1);
     cout << endl << c2.radius(); 
     Circle c3(Circle(4));
     cout << endl << c3.radius(); 
     return 0;
 }

Compiled with "g++ -std=c++0x". The output is:

Copy constructor with lvalue reference. Radius: 2
2
4

OK. The right constructors for the first two cases are called. But for the third case i.e., Circle c3(Circle(4)), I'd expect the third constructor, (copy constructor with rvalue referecne) to be called but it's not the case. Obviously some constructor is called since c3 is properly instantiated but I don't understand why the compiler is not using the explicitly provided one. Am I missing something here?

Beefeater answered 29/4, 2012 at 17:25 Comment(0)
T
9

In order to take the rvalue reference, it should be non-const, since the contents of the constructor argument will be moved and typically this is an operation that changes the state of the operand (although not in your particular case):

Circle(Circle&& c){ }

Also, you are seeing a copy elision here:

Circle c3(Circle(4));

so the move constructor doesn't get invoked. This is a standard compiler optimization that may or may not happen. If you were to construct a Circle like this:

Circle c3(std::move(c1));

then you would invoke the move constructor.

Turnstone answered 29/4, 2012 at 17:28 Comment(2)
I removed "const" from the constructor (and edited the question) but still the constructor is not called.Beefeater
@Amir I added an explanation, plus an expression that forces the call to the rvalue constructor.Turnstone
T
20

No move constructors are being called because your compiler is too smart for your code ;)

 Circle c1(2);

This just constructs an object with the int conversion constructor.

 Circle c2(c1);

This is a copy operation. c1 is an l-value, so it provokes a copy.

 Circle c3(Circle(4));

Here, your compiler recognizes that you're basically telling it to construct the object twice. So it elides one of the object constructors. This is allowed in this instance by the C++ specification.

If your compiler couldn't elide the construction, then it would perform an move. Also, if your compiler can't elide that, throw it away.

So there's no moving going on.

Trueblood answered 29/4, 2012 at 17:56 Comment(0)
T
9

In order to take the rvalue reference, it should be non-const, since the contents of the constructor argument will be moved and typically this is an operation that changes the state of the operand (although not in your particular case):

Circle(Circle&& c){ }

Also, you are seeing a copy elision here:

Circle c3(Circle(4));

so the move constructor doesn't get invoked. This is a standard compiler optimization that may or may not happen. If you were to construct a Circle like this:

Circle c3(std::move(c1));

then you would invoke the move constructor.

Turnstone answered 29/4, 2012 at 17:28 Comment(2)
I removed "const" from the constructor (and edited the question) but still the constructor is not called.Beefeater
@Amir I added an explanation, plus an expression that forces the call to the rvalue constructor.Turnstone
G
0

New compilers have a feature called RVO (Return value optimization) and try to avoid useless copies. In g++ if you put -fno-elide-constructors flag in your compile command it will avoid it. Nevertheless in your case even with this suppressing compiler flag, it will keep elide copy constructors and move constructors. If you want to see how your r-value constructor will be called use it as follow and compile it with above flag:

Circle MakeACircle(int r) {
      Circle temp(r);
      return temp;
}
Gelsenkirchen answered 23/6, 2021 at 11:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.