C++ move semantics
move semantics
C++11中引入了move semantics,它是基於[rvalue reference](C++ lvalue,rvalue及rvalue reference)來優化物件搬移效率的一種機制。
在一個自定義的類別中,如果我們希望使用move semantics,就必須為它定義兩個成員函數:move constructor及move assignment operator。
move constructor
move constructor的核心理念是:
- 把temporary object中的資料給搬到新物件中
- 將temporary object還原到一個valid state(可以想成0或NULL)
在TensorRT/samples/common/buffers.h
中:
//move constructor
//此處的GenericBuffer&&就是rvalue reference
GenericBuffer(GenericBuffer&& buf)
//獲取temporary object的成員屬性
: mSize(buf.mSize)
, mCapacity(buf.mCapacity)
, mType(buf.mType)
, mBuffer(buf.mBuffer)
{
//將temp object還原到一個valid state
buf.mSize = 0;
buf.mCapacity = 0;
buf.mType = nvinfer1::DataType::kFLOAT;
buf.mBuffer = nullptr;
}
注意此處move constructorGenericBuffer(GenericBuffer&& buf)
的輸入是一個rvalue reference。
第一步先將buf
的屬性給搬到新物件中,第二步則是將傳入的參數buf
清空,注意因為這裡是右值引用,所以我們才可以修改buf
。
move assignment operator
move assignment operator裡包含三步驟,相較於move constructor多了一步,即一開始需要將其現在的資料給清空:
- 把現有資料清空
- 把temporary object中的資料給搬到新物件中
- 將temporary object還原到一個valid state(可以想成0或NULL)
同樣來自TensorRT/samples/common/buffers.h
:
//move assignment operator
GenericBuffer& operator=(GenericBuffer&& buf)
{
if (this != &buf)
{
//清理現有的資料
freeFn(mBuffer);
//從temporary object中獲取資料
mSize = buf.mSize;
mCapacity = buf.mCapacity;
mType = buf.mType;
mBuffer = buf.mBuffer;
// Reset buf.
//將temporary state還原到一個valid state
//注意!在使用move assignment operator時等號右邊的GenericBuffer物件會被reset!
buf.mSize = 0;
buf.mCapacity = 0;
buf.mBuffer = nullptr;
}
return *this;
}
vector::emplace_back(move(unique_ptr))
同樣來自TensorRT/samples/common/buffers.h
:
//std::vector<std::unique_ptr<ManagedBuffer>> mManagedBuffers; //!< The vector of pointers to managed buffers
//std::unique_ptr<ManagedBuffer> manBuf{new ManagedBuffer()};
mManagedBuffers.emplace_back(std::move(manBuf));
由於manBuf
是unique_ptr,所以它不能被複製。
如果使用 v.emplace_back(manBuf)
的寫法,根據std::vector::emplace_back with lvalue expression它實際上會複製一份manBuf
再將它放入向量中,所以這肯定是會報錯的。
move semantics的存在讓我們可以移交物件的所有權而不需要另外複製一份。
為了使用move semantics,我們必須先將manBuf
由lvalue
轉換為rvalue
(rvalue
默認使用move semantics),而這就是std::move(manBuf)
在這裡的意義。以下是std::move
的定義:
template <class T>
typename remove_reference<T>::type&& move (T&& arg) noexcept;
它的作用是回傳其參數arg
的rvalue reference。
而vector
的emplace_back
的參數正好是rvalue reference:
template <class... Args>
void emplace_back (Args&&... args);
兩者正可遙相呼應。
參考連結
C++ rvalue references and move semantics for beginners
Why is move necessary with emplace_back in this example?
Pushing an object with unique_ptr into vector in C++