std::vector v = {2, 3, 3};
v = std::move(v); // undefined behavior
std::move 的函數原型.
/**
* @brief Convert a value to an rvalue.
* @param __t A thing of arbitrary type.
* @return The parameter cast to an rvalue-reference to allow moving it.
*/
template
constexpr typename std::remove_reference<_Tp>::type&&
move(_Tp&& __t) noexcept
{ return static_cast::type&&>(__t); }
結構體 remove_reference 的原型,就是重載了多個結構體模板來獲取原類型 type.
/// remove_reference
template
struct remove_reference
{ typedef _Tp type; };
template
struct remove_reference<_Tp&>
{ typedef _Tp type; };
template
struct remove_reference<_Tp&&>
{ typedef _Tp type; };
以下用兩個例子來說明std::move的用法.
– 原lvalue值被moved from之後值被轉移,所以為空字符串.
– 摘錄自cppreference
void TestSTLObject()
{
std::string str = "Hello";
std::vector v;
// uses the push_back(const T&) overload, which means
// we'll incur the cost of copying str
v.push_back(str);
std::cout << "After copy, str is \"" << str << "\"\n";
// uses the rvalue reference push_back(T&&) overload,
// which means no strings will be copied; instead, the contents
// of str will be moved into the vector. This is less
// expensive, but also means str might now be empty.
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";
}
輸出:
After copy, str is "Hello"
After move, str is ""
The contents of the vector are "Hello", "Hello"
– 自定義自己的類對象支持moved from 操作,需要實現 Move Constructors and Move Assignment Operators
#include
#include
#include
#include
#include
class MemoryBlock
{
public:
// Simple constructor that initializes the resource.
explicit MemoryBlock(size_t length)
: _length(length)
, _data(new int[length])
{
std::cout << "In MemoryBlock(size_t). length = "
<< _length << "." << std::endl;
}
// Destructor.
~MemoryBlock()
{
std::cout << "In ~MemoryBlock(). length = "
<< _length << ".";
if (_data != nullptr)
{
std::cout << " Deleting resource.";
// Delete the resource.
delete[] _data;
}
std::cout << std::endl;
}
// Copy constructor.
MemoryBlock(const MemoryBlock& other)
: _length(other._length)
, _data(new int[other._length])
{
std::cout << "In MemoryBlock(const MemoryBlock&). length = "
<< other._length << ". Copying resource." << std::endl;
std::copy(other._data, other._data + _length, _data);
}
// Copy assignment operator.
MemoryBlock& operator=(const MemoryBlock& other)
{
std::cout << "In operator=(const MemoryBlock&). length = "
<< other._length << ". Copying resource." << std::endl;
if (this != &other)
{
// Free the existing resource.
delete[] _data;
_length = other._length;
_data = new int[_length];
std::copy(other._data, other._data + _length, _data);
}
return *this;
}
// Retrieves the length of the data resource.
size_t Length() const
{
return _length;
}
// Move constructor.
MemoryBlock(MemoryBlock&& other)
: _data(nullptr)
, _length(0)
{
std::cout << "In MemoryBlock(MemoryBlock&&). length = "
<< other._length << ". Moving resource." << std::endl;
// Copy the data pointer and its length from the
// source object.
_data = other._data;
_length = other._length;
// Release the data pointer from the source object so that
// the destructor does not free the memory multiple times.
other._data = nullptr;
other._length = 0;
}
// Move assignment operator.
MemoryBlock& operator=(MemoryBlock&& other)
{
std::cout << "In operator=(MemoryBlock&&). length = "
<< other._length << "." << std::endl;
if (this != &other)
{
// Free the existing resource.
delete[] _data;
// Copy the data pointer and its length from the
// source object.
_data = other._data;
_length = other._length;
// Release the data pointer from the source object so that
// the destructor does not free the memory multiple times.
other._data = nullptr;
other._length = 0;
}
return *this;
}
private:
size_t _length; // The length of the resource.
int* _data; // The resource.
};
void TestSTLObject()
{
std::string str = "Hello";
std::vector v;
// uses the push_back(const T&) overload, which means
// we'll incur the cost of copying str
v.push_back(str);
std::cout << "After copy, str is \"" << str << "\"\n";
// uses the rvalue reference push_back(T&&) overload,
// which means no strings will be copied; instead, the contents
// of str will be moved into the vector. This is less
// expensive, but also means str might now be empty.
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";
}
void TestMyObjectWithoutUseMove()
{
std::vector v;
MemoryBlock mb1(25);
// MemoryBlock mb2(75);
// MemoryBlock mb3(50);
v.push_back(mb1);
//v.push_back(mb2);
//v.insert(v.begin() + 1, mb3);
}
void TestMyObjectWithUseMove()
{
std::vector v;
MemoryBlock mb1(25);
// MemoryBlock mb2(75);
// MemoryBlock mb3(50);
v.push_back(std::move(mb1));
//v.push_back(MemoryBlock(75));
//v.insert(v.begin() + 1, MemoryBlock(50));
}
int main(int argc, char const *argv[])
{
//TestSTLObject();
TestMyObjectWithoutUseMove();
std::cout << "......................................." << std::endl;
TestMyObjectWithUseMove();
return 0;
}
輸出:
1. 注意,第一個函數每個對象多調用了拷貝構造函數,多創建了一次,而使用了move操作的只是移動了資源
2. 注意,vector即使 push_back 第二個對象時,會移動第一個對象,很奇怪,如果你把注釋去掉的話,會發現資源 Moving 很多次,這是 vector 實現影響了,比較清楚的看出來 Move 的特性的就是 push_back 一個參數.
3. 注意,g++ 4.8.1 的 vector push_back 多個對象時優化的沒 vs 好,vs 是調用 Move 構造器,而 g++ 是調用 Copy 構造器,你會發現拷貝構造函數會調用很多次.
In MemoryBlock(size_t). length = 25.
In MemoryBlock(const MemoryBlock&). length = 25. Copying resource.
In ~MemoryBlock(). length = 25. Deleting resource.
In ~MemoryBlock(). length = 25. Deleting resource.
.......................................
In MemoryBlock(size_t). length = 25.
In MemoryBlock(MemoryBlock&&). length = 25. Moving resource.
In ~MemoryBlock(). length = 0.
In ~MemoryBlock(). length = 25. Deleting resource.