天天看点

C++ std::move()和std::forwardstd::move()std:forward

std::move()

只是做了个类型转换,它只保证转换后的结果是右值。

且并不是转换成了右值,就一定能保证发生移动而不是拷贝,如:

class Annotation {
public:
 explicit Annotation(const std::string text)
 : value(std::move(text)) // "move" text into value; this code
 { … } // doesn't do what it seems to!
 
 …
private:
 std::string value;
};
           

这里看似是把text通过std::move转换成了右值,然后调用string 的移动构造函数。

其实不然,注意到text是有const属性的,经std::move后它的const属性并没有丢掉,而string的移动构造函数形参为string&& rhs,没法接收const string &&作为实参,所以这里并不会调用移动构造函数。

反而是拷贝构造函数的形参为const string& rhs,可以接收const string &&,故最后会调用拷贝构造函数。

class string { // std::string is actually a
public: // typedef for std::basic_string<char>
 …
 string(const string& rhs); // copy ctor
 string(string&& rhs); // move ctor
 …
};
           

以上给了我们两点启发:

(1)如果想移动一个对象,不要把它设为const.因为对一个const对象的move请求会变成拷贝操作。

(2)std::move实际上不仅不移动任何东西,它还不保证转换后的结果能够被移动,它唯一保证的事情是转换的结果是右值。

std:forward

主要使用场景是一个函数模板形参为万能引用,且函数的功能是将该形参传递给其他函数,比如:

void process(const Widget& lvalArg);
void process(Widget&& rvalArg);

template<typename T>
void logAndProcess(T&& param){
	auto now=
		std::chrono::system_clock::now();
	makeLogEntry("Calling 'process'",now);
	process(std::forward<T>(param));
}
           

考虑分别传给logAndProcess左值和右值会发生什么

Widget w;
logAndProcess(w); // call with lvalue
logAndProcess(std::move(w)); // call with rvalue
           

传w,是左值,则会调用

void process(const Widget& lvalArg)

;

传std::move(w),是右值,我们想让它调用

void process(Widget&& rvalArg)

;但param在函数内是左值,会导致仍然调用

void process(const Widget& lvalArg)

,std::forward的作用就是,当param传进来的是右值时,保证传走的时候还是右值。至于怎么实现的,我的另一篇博客有写https://blog.csdn.net/xxxxxzz123/article/details/115972980。

总的来说,std::move和std::forward在运行时都不做任何事,他们其实就是在做类型转换,std::move是无条件地把实参转换为右值,std::forward是仅当传进来的实参是右值时,转换为右值(因为传进来以后都会变成左值,所以需要转换成右值)。

继续阅读