Home > Blockchain >  C does compiler automatically use std::move constructor for local variable that is going out of sc
C does compiler automatically use std::move constructor for local variable that is going out of sc

Time:03-27

#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.

  • Class 1: cost 1 copy constructor
  • Class 2: cost 1 copy constructor and 1 move constructor
  • Class 3: cost 1 copy constructor {A. nothing, B. move constructor, C. copy 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;
}

CodePudding user response:

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.

  • Related