c++ std::move 和 std::forward

  • 在C++11标准之前,C++中默认的传值类型均为Copy语义,即:不论是指针类型还是值类型,都将会在进行函数调用时被完整的复制一份。所以引入了move和forward
临时值(右值)简述
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是左值,但优先移动,不支持移动时仍可复制
}
使用 move
// 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)) {}
  1. 实际上,C++中的move函数只是做了类型转换,并不会真正的实现值的移动,因此对于自定义的类来说,如果要实现真正意义上的 “移动”,还是要手动重载移动构造函数和移动复制函数。即:我们需要在自己的类中实现移动语义,避免深拷贝,充分利用右值引用和std::move的语言特性。
  2. 通常情况下C++编译器会默认在用户自定义的class和struct中生成移动语义函数。但前提是:用户没有主动定义该类的拷贝构造等函数!
  3. 如果我们没有提供移动构造函数,只提供了拷贝构造函数,std::move()会失效但是不会发生错误,因为编译器找不到移动构造函数就去寻找拷贝构造函数,这也是拷贝构造函数的参数是const T&常量左值引用的原因
  4. c++11中的所有容器都实现了move语义,move只是转移了资源的控制权,本质上是将左值强制转化为右值使用,以用于移动拷贝或赋值,避免对含有资源的对象发生无谓的拷贝
  5. move对于拥有如内存、文件句柄等资源的成员的对象有效,如果是一些基本类型,如int和char[10]数组等,如果使用move,仍会发生拷贝(因为没有对应的移动构造函数),所以说move对含有资源的对象说更有意义。
foward 向前的,前进的;

完美转发是指在函数模板中,完全依照模板的参数的类型,将参数传递给函数模板中调用的另 一个函数,即传入转发函数的是左值对象,目标函数就能获得左值对象,转发函数是右值对象, 目标函数就能获得右值对象,而不产生额外的开销。

  1. 什么是foward?

    1. 问题:使用move后,处理临时变量用右值引用T&&,处理普通变量用const引用const T&,需要分别建立两个函数,然后入参使用不同的类型,每个函数都要写两遍。
    2. 能不能将T &&类型和const T &类型合二为一呢?
  2. std::forward也被称为完美转发,即:保持原来的值属性不变:

    1. 如果原来的值是左值,经std::forward处理后该值还是左值。如果外面传来了左值,它就转发左值并且启用copy,同时它也还能保留const。
    2. 如果原来的值是右值,经std::forward处理后它还是右值。如果外面传来了右值临时变量,它就转发右值并且启用move语义。

这样一来,我们就可以使用forward函数对入参进行封装,从而保证了入参的统一性,从而可以实现一个方法处理两种类型!
正因为如此,forward函数被大量用在了入参值类型情况不确定的C++模板中!

template<typename T>
void f_forward(T &&t) {
 
 
    Object a = std::forward<T>(t);//调用了std::forward<T>(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

move和forward函数的区别
  1. 基本上forward可以cover所有的需要move的场景,毕竟forward函数左右值通吃
  2. 那为什么还要使用move呢?原因主要有两点:
    1. 首先,forward函数常用于模板函数这种入参情况不确定的场景中,在使用的时候必须要多带一个模板参数forward,代码略复杂
    2. 此外,明确只需要move临时值的情况下如果使用了forward,会导致代码意图不清晰,其他人看着理解起来比较费劲
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: c++中的std::move和std::forward都是用于实现完美转发的工具。 std::move是将一个左值强制转换为右值引用,从而实现将资源所有权从一个对象转移到另一个对象的目的。使用std::move可以避免不必要的复制和赋值操作,提高程序的效率。 std::forward则是用于在函数模板中实现完美转发,将参数按照原来的类型转发给下一个函数。它可以保证参数的类型和值被完美地转发,避免了不必要的拷贝和移动操作,提高了程序的效率。 总的来说,std::move和std::forward都是用于提高程序效率和避免不必要的拷贝和移动操作的工具。 ### 回答2: C++标准库中提供了两个模板函数std::move和std::forward,它们在C++11中引入,用于实现移动语义和完美转发。 std::move的作用是将一个左值强制转换为右值引用,使得该对象的所有权能够被转移,而不是进行复制或者赋值。通过调用移动构造函数或者移动赋值运算符来减少开销。移动语义是C++11中的一个重要特性,它可以提高程序的效率并且使得程序更加高效。 std::forward的作用是实现完美转发,将函数参数原封不动地转发到另一个函数中,使得函数模板可以保持参数类型和实参类型一致。std::forward用于实现通用类型的泛型编程,解决了模板函数中参数类型无法确定的问题。 实际上,std::move和std::forward的实现方式都非常简单,都是使用了static_cast进行类型转换。但是它们在C++11中的引入,以及其实现的本质却给C++程序的效率提高和泛型编程提供了重要的支持。 总之,std::move和std::forwardC++11中非常重要的语言特性,它们可以帮助程序员实现移动语义和完美转发,提高程序的性能和可读性。要注意正确使用它们,以避免出现不必要的开销和错误。 ### 回答3: C++ 11中引入了两个新的特殊函数模板std::move()和std::forward(),用来实现完美转发和移动语义,提高了代码的效率和简洁性。 std::move的作用就是将一个左值转换成右值引用,将左值的所有权抢过来,但不进行任何内存拷贝。通常用于移动语义,可以提高程序的效率。用法很简单,就是std::move(左值变量)。比如,若有个vector<int> a和一个vector<int> b,我想把b中的元素全部移动到a中,可以这样写:a.insert(a.end(), std::make_move_iterator(b.begin()), std::make_move_iterator(b.end()));这里,std::make_move_iterator()是一个语法糖,将它们的元素包装成可以引用的右值。 std::forward的作用是保持参数本来的类型(左值或右值),既可以接收左值也可以接收右值,并将参数传递给其他函数,这就是所谓的完美转发。完美转发可以达到只有一个函数就可以处理所有情况的目的。用法就是std::forward<参数类型>(参数变量)。比如,若有个函数template<class T> void f(T&& t),其中参数t是万能引用,需要把t传递给其他函数g(),我们可以这样写:g(std::forward<T>(t));这样就可以达到完美转发的目的。 需要注意的是,std::move和std::forward虽然看起来相似,但作用是不同的,std::move是将左值转换成右值引用,而std::forward是维持参数的原类型,用于完美转发。同时,它们都需要加上相应的模板类型,以便让编译器进行类型推导。在使用时,需要根据情况选择合适的函数,以达到更好的效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值