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