【Effective Modern C++】Chapter 5 右值引用、移动语义和完美转发

本文详细介绍了C++中的右值引用、移动语义和完美转发的概念及其应用场景。移动语义用于优化昂贵的复制操作,而完美转发则确保函数模板能够准确传递参数。std::move无条件转换为右值,std::forward则有条件地仅在实参为右值时转换。万能引用是一种特殊的引用,既能绑定右值也能绑定左值。理解这些概念对于高效C++编程至关重要。
摘要由CSDN通过智能技术生成

Chapter 5 右值引用、移动语义和完美转发

移动语义:用移动操作来替代昂贵的复制操作。

完美转发:函数模板转发其他函数,目标函数将接收到与转发函数所接受的完全相同的实参。

右值引用:让移动语义和完美转发成为可能。

实际情况:

  • std::move其实并没有移动任何东西…
  • 完美转发并不完美…
  • 移动操作的成本并不一定比复制低…
  • 移动语境中不一定能调到移动操作…
  • "type&&"并不总是表示右值引用…

C++规则:形参总是左值,即使其型别是右值引用。

23 理解std::move和std::forward

std::move

std::move是无条件的将实参强制转换成右值的函数模板。

C++14中std::move的实现类似:

template<typename T>
decltype(auto) move(T&& param)
{
	using ReturnType = remove_reference_t<T>&&;
	return static_cast<ReturnType>(param);
}

移动失败的场景:下面的代码看似使用了移动,实则是复制,原因在于text具有const属性,因此代码不应该允许常量对象传递到有可能改动他们的函数中(比如移动构造函数)。

class Base {
public:
	explicit Base(const std::string text) : value(std::move(text)){
	}
private:
	std::string value;
}
std::forward

std::forward是有条件的将实参强制转换成右值的函数模板。

典型场景如下,C++规则:形参总是左值 ,如果下面的代码中不加std::forward,则process拿到的参数一定是左值,而std::forward做的事情就是仅当用来初始化param的实参是个右值时,把param强制转换为右值型别。

void process(const string& lVal) {
    cout << "left" << endl;
}

void process(string&& rVal) {
    cout << "right" << endl;
}

template<typename T>
void function(T&& param) {
    // C++规则:形参总是左值 
    // 故当传入的param是右值时需要强制转换为右值才能实现完美转发
    process(std::forward<T>(param));
}

int main() {
    string s{"Hello."};
    function(s);  // 调用打印left的函数
    function(std::move(s));  // 调用打印right的函数

    return 0;
}

二者的区别:

  • std::move无条件的将实参强制转换成右值;
  • std::forward仅在特定条件(传入的实参被绑定到右值)下执行强制转换。

为什么std::forward不能代替std::move?

  1. 方便:std::move只需要实参,std::forward需要实参和模板类型。
  2. 减少错误的可能:对于实参是左值引用时,用std::move转换完可顺利调用移动构造函数,而std::forward转换后因为没有变为右值,则调用复制构造函数,与初衷背离。
  3. 清晰:std::move的含义时无条件强制转换,std::forward的含义是仅对实参是右值引用时才强制转换。
要点速记
  • std::move实施的是无条件的向右值型别的强制转换,就其本身而言,它不会执行移动操作。
  • 仅当传入的实参被绑定到右值时,std::forward才针对该实参实施向右值性别的强制转换。
  • 在运行期间,std::move和std::forward不会做任何操作。

24 区分万能引用和右值引用

万能引用:既可以是右值引用,又可以是左值引用;既可以绑定到const对象,又可以绑定非const对象;volatile对象和非volatile对象同理;甚至const和volatile同时修饰也同理。

万能引用需要具备两个条件:

  1. 必须是精确的T&&类型;
  2. 2.T的型别必须由推导而来。
// 1. 必须是精确的T&&类型
template<typename T>
void func(std::vector<T>&& param);  // 不是万能引用,是右值引用
void func(const T&& param);  // 不是万能引用,是右值引用

// 2.T的型别必须由推导而来
auto&& var2 = var1;  // 万能引用
template<typename T>
void func(T&& param);  // 万能引用

// 常见右值引用
void f(Base&& param);  // 右值引用
Base&& var1 = Base();  // 右值引用
template<typename T>
void func(std::vector<T>&& param);  // 右值引用
要点速记
  • 如果函数模板形参具备T&&型别,并且T的型别由推导而来,或如果对象使用auto&&声明其型别,则该形参或对象就是万能引用。
  • 如果型别声明并不精确地具备T&&的形式,或者型别推导并未发生,则T&&就代表右值引用。
  • 若采用右值来初始化万能引用,就会得到一个右值引用;若采用左值来初始化万能引用,就会得到一个左值引用。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值