std::move将对象标记为可变的右值对象。根据引用折叠原则,std::move的参数可以为右值引用,那么这个函数将什么都不做。
std::move的实现非常简单暴力:
namespace std
{
template<typename T>
struct CRemoveReference
{
using Type = T;
};
template<typename T>
struct CRemoveReference<T&>
{
using Type = T;
};
template<typename T>
struct CRemoveReference<T&&>
{
using Type = T;
};
template<typename T>
using remove_reference_t =typename CRemoveReference<T>::Type;
template<typename T>
constexpr remove_reference_t<T>&& move(T&& t) noexcept
{
return static_cast<remove_reference_t<T>&&>(t);
}
}
右值的作用是什么呢?
为了更好的避免内存(尤其是大内存)拷贝和冗余式地释放和申请,允许类设计者通过定义移动拷贝构造函数/移动赋值运算符函数。
A a1;
A a2 = std::move( a1 );
a1 = std::move(a2);
第二行语句将执行移动拷贝构造(move constructor):
- 创建一片大小为sizeof(A)的内存;
- 将a1中 内容拷贝到这片内存中;
- a1中的有效内存被销毁。
注意:如果不自定义move-constructor,那么将调用默认的copy-constructor。如果对象中有堆内存管理,必须定义move-constructor。
第三句将执行移动赋值运算符(move assigment operator)函数。
下面的例子:
test函数中的
- 第一个语句,相当于a调用构造函数创建了一个对象;
- 第二个语句,在getA2中创建了一个临时对象,又调用A的move-constructor创建了b,再销毁了临时对象.
- 第三个语句,会强制进行move-constructor,整体同语句2
- 第四个语句,std::move的参数如果是A&& ,则相当于什么都不做,所以和语句2相同。
A getA1( )
{
return A();
}
A getA2( )
{
A a;
a.x = 123;
a.y = 456;
}
void test( )
{
A a = getA1( );
A b = getA2( );
A a = std::move( getA1( ) );
A b = std::move( getA2( ) );
}
自定义move-constrctor和move-assigment-operator样例
struct Thing
{
Thing( )noexcept :
_data(new int[100])
{
std::cout << "default construct\n";
}
// Copy operator
Thing(const Thing& other)noexcept :
_data(new int[100])
{
std::cout << "copy constructor\n";
memcpy(_data, other._data, sizeof(int) * 100);
}
// Move constructor
Thing(Thing&& other) noexcept :
_data(other._data)
{
std::cout << "move constructor\n";
other._data = nullptr;
}
// assignment operator
Thing& operator=(const Thing& rhs) noexcept
{
std::cout << "copy operator\n";
if (&rhs != this)
{
Thing tmp(rhs);
std::swap(*this, tmp);
}
return *this;
}
// move assignment operator
Thing& operator=(Thing&& rhs) noexcept
{
std::cout << "move operator\n";
std::swap(_data, rhs._data);
return *this;
}
// destructor necessary since we are working in dangerous new/delete territory
~Thing() noexcept
{
std::cout << "destructor " << (_data ? "object has data" : "object is empty") << "\n";
delete[] _data;
}
private:
int* _data;
};
int main2()
{
std::cout << "constructing a\n";
Thing a;
std::cout << "constructing b with copy of a\n";
Thing b(a);
std::cout << "moving a to newly constructed c\n";
Thing c(std::move(a));
std::cout << "moving c back to a\n";
a = std::move(c);
std::cout << "create a new d\n";
Thing d;
std::cout << "replace d with a copy of a\n";
d = a;
return 0;
}
输出:
constructing a
default construct
constructing b with copy of a
copy constructor
moving a to newly constructed c
move constructor
moving c back to a
move operator
create a new d
default construct
replace d with a copy of a
copy operator
copy constructor
move constructor
move operator
move operator
destructor object is empty
destructor object has data
destructor object has data
destructor object is empty
destructor object has data
destructor object has data