C++ std::move


std::move 是 C++ 标准库中定义在 <utility> 头文件里的一个非常实用的工具函数,它的主要作用是将一个左值强制转换为右值引用,从而可以触发移动语义。以下是对 std::move 源码的详细解析。

源码形式

在 C++ 标准库中,std::move 的实现通常如下(简化示意,不同编译器可能存在细微差异,但核心逻辑一致):

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

源码解析

模板参数 T
  • T 是一个模板类型参数,它可以根据函数调用时传入的参数进行自动推导。std::move 函数使用了“万能引用”(T&&)的形式来接收参数。“万能引用”是一种特殊的引用类型,它既可以绑定左值,也可以绑定右值。
  • 当传入的是左值时,T 会被推导为左值引用类型;当传入的是右值时,T 会被推导为非引用类型。例如:
int num = 10;
std::move(num); // 这里 T 被推导为 int&
std::move(20);  // 这里 T 被推导为 int
std::remove_reference<T>::type
  • std::remove_reference 是 C++ 标准库中的一个类型特征(type trait),定义在 <type_traits> 头文件中。它的作用是去除类型 T 的引用属性,返回一个非引用类型。
  • 例如,如果 Tint&,那么 std::remove_reference<T>::type 就是 int;如果 Tint&&std::remove_reference<T>::type 同样是 int。在 std::move 的实现中使用它,是为了确保最终返回的类型是一个右值引用类型,而不受 T 原本是否为引用类型的影响。
函数参数 T&& arg
  • 这是一个“万能引用”参数,它可以接受任何类型的值,无论是左值还是右值。通过这种方式,std::move 函数可以灵活地处理各种输入情况。
  • 当传入左值时,arg 绑定到左值;当传入右值时,arg 绑定到右值。
static_cast<typename std::remove_reference<T>::type&&>(arg)
  • static_cast 是 C++ 中的强制类型转换运算符,这里使用它将参数 arg 强制转换为 typename std::remove_reference<T>::type&& 类型,也就是一个右值引用类型。
  • 例如,当传入左值 num 时,T 被推导为 int&std::remove_reference<T>::typeint,最终将 num 强制转换为 int&& 类型,从而将左值转换为右值引用。
  • 当传入右值 20 时,T 被推导为 intstd::remove_reference<T>::type 还是 int,同样将 20 以右值引用的形式返回。
noexcept 说明符
  • noexcept 表示该函数不会抛出异常。这是为了让编译器在调用 std::move 时进行一些优化,例如在移动构造函数或移动赋值运算符中使用 std::move 时,编译器可以更放心地进行资源转移操作,因为它知道这个过程不会因为异常而中断。

总结

std::move 的核心思想就是利用模板类型推导和类型转换,将传入的参数强制转换为右值引用类型。这样做的目的是为了在合适的场景下触发移动语义,避免不必要的深拷贝,提高程序的性能。例如,在容器的元素插入操作中,如果使用 std::move 将对象转换为右值引用,就可以调用对象的移动构造函数或移动赋值运算符,从而更高效地完成元素的插入操作。

#include <iostream>
#include <utility>
#include <vector>

class MyClass
{
public:
    // 默认构造函数
    MyClass() : data(new int[1000])
    {
        std::cout << "Default constructor" << std::endl;
    }

    // 拷贝构造函数
    MyClass(const MyClass& other) : data(new int[1000]) 
    {
        std::cout << "Copy constructor" << std::endl;
        for (int i = 0; i < 1000; ++i) {
            data[i] = other.data[i];
        }
    }

    // 移动构造函数
    MyClass(MyClass&& other) noexcept : data(other.data)
    {
        std::cout << "Move constructor" << std::endl;
        other.data = nullptr;
    }

    // 析构函数
    ~MyClass() {
        delete[] data;
    }

private:
    int* data;
};

int main()
{
    MyClass obj1;  // std::cout << "Copy constructor" << std::endl;
    std::vector<MyClass> vec;

    // 使用 std::move 触发移动构造函数
    vec.push_back(std::move(obj1));//  std::cout << "Move constructor" << std::endl;

    return 0;
}

那么问题来了,它的主要作用是将一个左值强制转换为右值引用,如果传入参数是一个右值引用呢?
根据源码
_EXPORT_STD template
_NODISCARD _MSVC_INTRINSIC constexpr remove_reference_t<_Ty>&& move(_Ty&& _Arg) noexcept {
return static_cast<remove_reference_t<_Ty>&&>(_Arg);
先萃取类型,然后强转成右值引用 , 所以无论传入左值还是右值,std:move都会将其转成右值

#include <iostream>
#include <utility>
#include <vector>

class MyClass
{
public:
    // 默认构造函数
    MyClass() : m(100)
    {
        std::cout << "Default constructor" << std::endl;
    }

    // 拷贝构造函数
    MyClass(const MyClass& other) : m(100)
    {
        std::cout << "Copy constructor" << std::endl;
    }

    // 移动构造函数
    MyClass(MyClass&& other) noexcept : m(other.m)
    {
        std::cout << "Move constructor" << std::endl;

    }

    // 析构函数
    ~MyClass() {

    }

public:

    int m;
};

int main()
{
    MyClass obj1;  // std::cout << "Copy constructor" << std::endl;
    std::vector<MyClass> vec;

    // 使用 std::move 触发移动构造函数
    vec.push_back(std::move(obj1));//  std::cout << "Move constructor" << std::endl;
    vec.push_back(  std::move(  MyClass()  )  );//  std::cout << "Move constructor" << std::endl;
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值