Huy Le
Huy Le

Reputation: 1738

C++ does compiler automatically use std::move constructor for local variable that is going out of scope?

#include <iostream>
#include <string>
using namespace std;

class Class1 {
    string s;
public:
    Class1(const string& s_) : s(s_) {}        
};

class Class2 {
    string s;
public:
    Class2(string s_) : s(std::move(s_)) {}
};

class Class3 {
    string s;
public:
    Class3(string s_) : s(s_) {}
};


int main()
{
    string str = "ABC";
    Class1 a(str);
    Class2 b(str);
    Class3 c(str);
}

I'm trying to find the cost of my constructor.

Is the compiler smart enough to copy str directly into c.s ? What is the answer for the 3rd case?

Big edit: using a vector-like class, the answer is C. But is there any reason why the compiler can't steal s_ even when the object will be destructed after s(s_) ?

#include <iostream>
#include <string>
using namespace std;

template <typename T = int>
class MyVector {
private:
    int n;
    T* data;
public:
    MyVector() {
        n = 0;
        data = nullptr;
        cout << "MyVector default constructor\n";
    }

    MyVector(int _n) {
        n = _n;
        data = new T[n];
        cout << "MyVector param constructor\n";
    }

    MyVector(const MyVector& other) {
        n = other.n;
        data = new T[n];
        for (int i=0; i<n; i++) data[i] = other.data[i];
        cout << "MyVector copy constructor\n";
    }

    MyVector(MyVector&& other) {
        n = other.n;
        data = other.data;
        other.n = 0;
        other.data = nullptr;
        cout << "MyVector move constructor\n";
    }

    MyVector& operator = (const MyVector& other) {
        if (this != &other) {
            n = other.n;
            delete[] data;
            data = new T[n];
            for (int i=0; i<n; i++) data[i] = other.data[i];
        }
        cout << "MyVector copy assigment\n";
        return *this;
    }

    MyVector& operator = (MyVector&& other) {
        if (this != &other) {
            n = other.n;
            delete[] data;
            data = other.data;
            other.n = 0;
            other.data = nullptr;
        }
        cout << "MyVector move assigment\n";
        return *this;
    }

    ~MyVector() {
        delete[] data;
        cout << "MyVector destructor: size = " << n << "\n";
    }

    int size() {
        return n;
    }
};

class Class1 {
    MyVector<> s;
public:
    Class1(const MyVector<>& s_) : s(s_) {}        
};

class Class2 {
    MyVector<> s;
public:
    Class2(MyVector<> s_) : s(std::move(s_)) {}
};

class Class3 {
    MyVector<> s;
public:
    Class3(MyVector<> s_) : s(s_) {}
};


int main()
{
    MyVector<> vec(5);
    cout << "-----------\n";
    
    cout << "Class1\n";
    Class1 a(vec);
    cout << "\n------------\n";

    cout << "Class3\n";
    Class2 b(vec);
    cout << "\n------------\n";

    cout << "Class3\n";
    Class3 c(vec);
    cout << "\n------------\n";

    return 0;
}

Upvotes: 1

Views: 705

Answers (1)

user17732522
user17732522

Reputation: 76658

s_ is a lvalue in the initializer s(s_). So the copy constructor will be used to construct s. There is not automatic move like e.g. in return statements. The compiler is not allowed to elide this constructor call.

Of course a compiler can always optimize a program in whatever way it wants as long as observable behavior is unchanged. Since you cannot observe copies of a string being made if you don't take addresses of the string objects, the compiler is free to optimize the copies away in any case.

If you take a constructor argument to be stored in a member by-value, there is no reason not to use std::move.

Upvotes: 1

Related Questions