std::move
看功能先看源码:
namespace std {
template <typename T>
typename std::remove_reference<T>::type&& move(T&& arg) noexcept {
return static_cast<typename std::remove_reference<T>::type&&>(arg);
}
可以看到其返回值进行了一个static的强制类型转化,并且又用到了std::remove_reference,那再看看这个源码是什么:
template <typename T>
struct remove_reference {
using type = T;
};
template <typename T>
struct remove_reference<T&> {
using type = T;
};
template <typename T>
struct remove_reference<T&&> {
using type = T;
};
template <typename T>
using remove_reference_t = typename remove_reference<T>::type;
从这个代码可以看出,无论给remov_reference传入什么类型(即T类型,T的右值引用,T的左值引用),都会定义一个别名为type的T类型,作用显而易见就是去掉引用(&&或&),意义在于移除类型的引用修饰,返回对应的非引用类型。
回到std::move上,他可以使move的返回类型为std::remove_reference<_TP>::type&&,简化来看就是T的右值引用,通过代码内部的static_cast转换而来。
于是我们可以简化一下std::move函数
template <typename T>
typename T&& move(T&& arg){
return T&&(arg);
}
那么了解了源码之后,我就碰到下面的问题:
假设A,B是两个数组,且数组元素都是可移动类型的,例如std::vector;
那么对于
case1:
A = std::move(B);
cout << B[1] << endl;
case2:
std::move(B);
cout << B[1] << endl;
这两种情况执行cout这一句会报错吗?
先看case2,这是一个临时表达式,返回的是一个右值引用,表示B
可以被移动。这条语句本身并不会对B
造成实际影响,它只是将B
转换为右值引用,这条语句本身不会影响数组B
。因此,访问B[1]
应该能够正常编译和执行。
那么看case1,多了个A =
,则会触发调用移动赋值操作符,将数组B
的内容移动到数组A
中,对于移动赋值操作,可以简单康康其源码:
class MyClass {
public:
// 构造函数
MyClass(int val) : data(new int(val)) {}
// 移动赋值操作符
MyClass& operator=(MyClass&& other) noexcept {
if (this != &other) { // 检查是否为同一对象
delete data; // 释放当前对象的资源
data = other.data; // 移动资源所有权
other.data = nullptr; // 设置原对象为无效状态
}
return *this;
}
private:
int* data;
};
其意义在于:
构造函数 MyClass(int val):这是类的构造函数,它接受一个整数值 val,并在堆上分配一个 int 类型的资源,将 val 的值存储在这个资源中。
移动赋值操作符 operator=(MyClass&& other):这是移动赋值操作符,用于将一个右值引用的 MyClass 对象的资源移动给当前对象。在这个操作符的实现中:
1、首先,它检查当前对象是否与右值引用 other 是同一个对象,以避免不必要的操作。
2、然后,它释放当前对象的资源,通过 delete data; 来释放之前分配的资源,以确保当前对象在获得新资源的同时,不再持有旧资源。通过释放旧资源,可以防止内存泄漏,并确保资源的正确管理。
3、接着,它将 other 的资源指针赋值给当前对象的 data 成员,即移动了资源的所有权。
4、最后,它将 other 的资源指针设置为 nullptr,将原对象置于无效状态,以避免 other 在析构时释放已经移动的资源。
从这个移动赋值操作可以看出,意味着B
的内容会被转移给A
,并且B
的内容会被置为移动后的有效但未指定的状态。在执行完这条语句后,B
应该是不再持有有效的数据。
总而言之,std::move本身只会进行强制类型转换,变成右值,如果没有上下文的影响,其可以照常像右值一样使用,如果进行了将其对其他具名变量赋值操作之后,其实也就是触发了所谓的“移动语义”,资源就会转移到像上述A
中,本身资源状态可能就会变成无效的了。
问题及解答来源:面试经验。