std::move原理实现与用法总结

  右值引用(及其支持的Move语意和完美转发)是C++0x加入的最重大语言特性之一。从实践角度讲,它能够完美解决C++中长久以来为人所诟病的临时对象效率问题。从语言本身讲,它健全了C++中的引用类型在左值右值方面的缺陷。从库设计者的角度讲,它给库设计者又带来了一把利器。从库使用者的角度讲,不动一兵一卒便可以获得“免费的”效率提升。

一、左值与右值

  • 左值:指表达式结束后依然存在的持久对象可以取地址,具名变量或对象 。

  • 右值:表达式结束后就不再存在的临时对象不可以取地址,没有名字。

int a;
int b;

a = 3;
b = 4;
a = b;
b = a;

// 以下写法不合法。
3 = a;
a + b = 4;

二、左值引用和右值引用

  引用是C++语法做的优化,引用的本质还是靠指针来实现的。引用相当于变量的别名。引用可以改变指针的指向,还可以改变指针所指向的值。

  • 左值引用:type &引用名 = 左值表达式;

  • 右值引用:type &&引用名 = 右值表达式;

三、std::move详解

1、std::move简介

  在C++11中,标准库在中提供了一个有用的函数std::move,std::move并不能移动任何东西,它唯一的功能是将一个左值引用强制转化为右值引用,继而可以通过右值引用使用该值,以用于移动语义。从实现上讲,std::move基本等同于一个类型转换:static_cast<T&&>(lvalue);

#include <iostream>
#include <utility>
#include <vector>
#include <string>
int main()
{
    std::string str = "Hello";
    std::vector<std::string> v;
    //调用常规的拷贝构造函数,新建字符数组,拷贝数据
    v.push_back(str);
    std::cout << "After copy, str is \"" << str << "\"\n";
    //调用移动构造函数,掏空str,掏空后,最好不要使用str
    v.push_back(std::move(str));
    std::cout << "After move, str is \"" << str << "\"\n";
    std::cout << "The contents of the vector are \"" << v[0]
                                         << "\", \"" << v[1] << "\"\n";
}

2、std::move详解

  std::move 的函数原型定义:

template <typename T>
typename remove_reference<T>::type&& move(T&& t)
{
	return static_cast<typename remove_reference<T>::type &&>(t);
}

  首先,函数参数T&&是一个指向模板类型参数的右值引用,通过引用折叠,此参数可以与任何类型的实参匹配(可以传递左值或右值,这是std::move主要使用的两种场景)。关于引用折叠如下:

  • 所有右值引用折叠到右值引用上仍然是一个右值引用。(A&& && 变成 A&&) 。

  • 所有的其他引用类型之间的折叠都将变成左值引用。 (A& & 变成 A&; A& && 变成 A&; A&& & 变成 A&)。

  简单来说,右值经过T&&传递类型保持不变还是右值,而左值经过T&&变为普通的左值引用。

//原始的,最通用的版本
template <typename T> struct remove_reference{
    typedef T type;  //定义T的类型别名为type
};
 
//部分版本特例化,将用于左值引用和右值引用
template <class T> struct remove_reference<T&> //左值引用
{ typedef T type; }
 
template <class T> struct remove_reference<T&&> //右值引用
{ typedef T type; }   
  
//举例如下,下列定义的a、b、c三个变量都是int类型
int i;
remove_refrence<decltype(42)>::type a;             //使用原版本,
remove_refrence<decltype(i)>::type  b;             //左值引用特例版本
remove_refrence<decltype(std::move(i))>::type  b;  //右值引用特例版本 

  std::move实现,首先,通过右值引用传递模板实现,利用引用折叠原理将右值经过T&&传递类型保持不变还是右值,而左值经过T&&变为普通的左值引用,以保证模板可以传递任意实参,且保持类型不变。然后我们通过static_cast<>进行强制类型转换返回T&&右值引用而static_cast之所以能使用类型转换,是通过remove_refrence::type模板移除T&&,T&的引用,获取具体类型T(模板偏特化)。

3、std::move的优点

  • std::move语句可以将左值变为右值而避免拷贝构造

  • std::move是将对象的状态或者所有权从一个对象转移到另一个对象,只是转移,没有内存的搬迁或者内存拷贝。

`std::move` 是 C++11 中引入的一个工具函数,位于头文件 `<utility>` 中。它的作用是将一个左值 (lvalue) 转换为右值 (rvalue),以便触发移动语义 (Move Semantics)。虽然名为“移动”,但它本身并不会真正地移动数据,而是仅仅改变表达式的类别,使得编译器可以调用对应的移动构造函数或移动赋值运算符。 --- ### 实现原理 #### 核心机制 `std::move` 的本质是一个类型转换工具,其实现依赖于 `static_cast` 和转发引用技术: ```cpp template<class T> constexpr typename std::remove_reference<T>::type&& move(T&& t) noexcept { return static_cast<typename std::remove_reference<T>::type&&>(t); } ``` - **模板参数推导**:`T&&` 并不一定表示这是一个 rvalue 引用,实际上它可以接受 lvalue 或 rvalue(这被称为通用引用)。因此,无论传入的是 lvalue 还是 rvalue,都可以匹配到这个模板。 - **类型调整**:通过 `std::remove_reference<T>::type` 去掉原始类型的引用属性,并将其强制转换为右值引用形式 (`T&&`)。这种显式转换告诉编译器,“我希望把这个对象当作右值对待”。 #### 示例代码解释 下面的例子说明了 `std::move` 的实际用途: ```cpp #include <iostream> #include <vector> #include <utility> void example() { std::vector<int> vec = {1, 2, 3}; // 正常传递给函数会调用拷贝构造函数 void func(const std::vector<int>& v); // 使用 std::move 后,vec 将被视为临时变量(xvalue) func(std::move(vec)); // 注意此时 vec 已经被"移动", 它的状态未定义但仍合法存在 } // 输出结果取决于是否支持 Move 构造函数的具体实现细节... ``` 在这个例子中,如果没有 `std::move` ,那么整个容器会被深拷贝;而有了之后,则可能直接交换内部存储地址等信息,极大减少了开销。 ---
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值