func("some temporary string"); // 尽管直接将一个常量传入函数中, C++还是大概率会创建一个string的复制
v.push_back(X()); // 初始化了一个临时X, 然后被复制进了vector
a = b + c; // b+c是一个临时值, 然后被赋值给了a
x++; // x++操作也有临时变量的产生(++x则不会产生)
a = b + c + d; //c+d是一个临时变量, b+(c+d)是另一个临时变量
vector<string> str_split(const string& s) {
vector<string> v;
// ...
return v; // v是左值,但优先移动,不支持移动时仍可复制
}
// Copy constructor
MyString(const MyString &str) {}
// Move constructor
MyString(MyString &&str) noexcept {}
// Copy assignment
MyString& operator=(const MyString& str) {}
// Move assignment
MyString& operator=(MyString&& str) {}
//使用std::move
void f_move(Object &&obj) {}
Object(Object &&object) noexcept: _str(std::move(object._str)) {}
只是做了类型转换,并不会真正的实现值的移动,因此对于自定义的类来说,如果要实现真正意义上的 “移动”,还是要手动重载移动构造函数和移动复制函数。即:我们需要在自己的类中实现移动语义,避免深拷贝,充分利用右值引用和std::move的语言特性。编译器会默认在用户自定义的class和struct中生成移动语义函数。但前提是:用户没有主动定义该类的拷贝构造等函数!没有提供移动构造函数,只提供了拷贝构造函数,std::move()会失效但是不会发生错误,因为编译器找不到移动构造函数就去寻找拷贝构造函数,这也是拷贝构造函数的参数是const T&常量左值引用的原因move只是转移了资源的控制权,本质上是将左值强制转化为右值使用,以用于移动拷贝或赋值,避免对含有资源的对象发生无谓的拷贝完美转发是指在函数模板中,完全依照模板的参数的类型,将参数传递给函数模板中调用的另 一个函数,即传入转发函数的是左值对象,目标函数就能获得左值对象,转发函数是右值对象, 目标函数就能获得右值对象,而不产生额外的开销。
什么是foward?
std::forward也被称为完美转发,即:保持原来的值属性不变:
这样一来,我们就可以使用forward函数对入参进行封装,从而保证了入参的统一性,从而可以实现一个方法处理两种类型!
正因为如此,forward函数被大量用在了入参值类型情况不确定的C++模板中!
template<typename T>
void f_forward(T &&t) {
Object a = std::forward<T>(t);//调用了std::forward(t)来创建一个新的对象。
std::cout << "forward this object, address: " << &a << std::endl;
}
int main() {
Object obj{"abc"};
//分别使用一个左值和一个右值调用了该模板函数。
f_forward(obj);
f_forward(Object("def"));
return 0;
}
build this object, address: 000000CFAE8FFC78
copy this object, address: 000000CFAE8FFBD8
forward this object, address: 000000CFAE8FFBD8
destruct this object, address: 000000CFAE8FFBD8
build this object, address: 000000CFAE8FFCB8
move this object!
forward this object, address: 000000CFAE8FFBD8
destruct this object, address: 000000CFAE8FFBD8
destruct this object, address: 000000CFAE8FFCB8
destruct this object, address: 000000CFAE8FFC78