RuLoViC
RuLoViC

Reputation: 855

Move semantics in C++11

I would like to fully understand move semantics in C++11. So I have written a couple of classes to see when different constructors are called:

#include <iostream>
using namespace std;

class A {
public:
    A() : a1_(0) {std::cout << "Calling constructor" << std::endl;}
    A(A&& other) {
        std::cout << "Calling move constructor" << std::endl;
        a1_ = other.a1_;
        other.a1_ = 0;
    }

    // Move assignment operator.
    A& operator=(A&& other) {
        std::cout << "Calling move operator" << std::endl;
        if (this != &other) {
            a1_ = other.a1_;
            other.a1_ = 0;
        }
        return *this;
    }

    // Copy constructor.
    A(const A& other) {
        std::cout << "Calling copy constructor" << std::endl;
        a1_ = other.a1_;
    }

    // Copy assignment operator.
    A& operator=(const A& other) {
        std::cout << "Calling copy assignment operator" << std::endl;
        if (this != &other) {
            a1_ = other.a1_;
        }
        return *this;
    }

private:
    int a1_;
};

class B {
    A oA_;

public:
    B() {}
    void setoA(A a) {oA_ = a;}
        A getoA() {return oA_;}
};

A createA() {
    A a1;
    return a1;
}

B createB() {
    B tmpB;
    A tmpA;
    tmpB.setoA(tmpA);
    return tmpB;
}

int main() {
    B b;
    A a;
    b.setoA(a);
    std::cout << "**************************" << std::endl;
    b.setoA(createA());
    std::cout << "**************************" << std::endl;
    b.setoA(std::move(createA()));
    std::cout << "**************************" << std::endl;
    B b2;
    b2.setoA(b.getoA());
    std::cout << "**************************" << std::endl;
    createB();

    return 0;
}

When I check the output of this code:

    Calling constructor

    Calling constructor

    Calling copy constructor

    Calling copy assignment operator

    ++++++++++++++++++++++++++++++++++

    Calling constructor

    Calling copy assignment operator

    ++++++++++++++++++++++++++++++++++

    Calling constructor

    Calling move constructor

    Calling copy assignment operator

    ++++++++++++++++++++++++++++++++++

    Calling constructor

    Calling copy constructor

    Calling copy assignment operator

    ++++++++++++++++++++++++++++++++++

    Calling constructor

    Calling constructor

    Calling copy constructor

    Calling copy assignment operator

I have some doubts here:

I thought that if you pass r-value, move constructor will be called, is that right? Isn't this b.setoA(createA()); an r-value?

How can I make move constructor/operator being called?

Upvotes: 0

Views: 319

Answers (2)

Brian Bi
Brian Bi

Reputation: 119184

Some copies and moves may be optionally elided by the compiler. To prevent this, use -fno-elide-constructors in GCC and Clang. In addition, some move elisions became mandatory in C++17, so to force the compiler to use C++11 move semantics, use -std=c++11 as well.

Upvotes: 0

Aconcagua
Aconcagua

Reputation: 25526

First of all in first section, why is constructor being called twice?

Because you construct both an B and a A with the former having its own instance of A, which the first (the unexpected) constructor call comes from.

I thought that if you pass r-value move constructor will be called, is that right? Isn't this b.setoA(createA()); an r-value?

The constructor is called from within createA (and yes, return value is an r-value), however, copy elision occurs and the object is directly instantiated in the parameter variable of setoA.

Within setoA, however, the copy assignment is selected, as now a is an l-value. If you want to move, you need:

void setoA(A a) { oA_ = std::move(a); }

Upvotes: 6

Related Questions