簡體   English   中英

為什么std :: vector有兩個賦值運算符?

[英]Why does std::vector have two assignment operators?

自2011年以來,我們同時擁有復制和移動任務。 但是, 這個答案非常有說服力地說,對於資源管理類,只需要一個賦值運算符。 例如,對於std::vector ,這看起來像

vector& vector::operator=(vector other)
{
  swap(other);
  return*this;
}

這里重要的一點是,論證是以價值為基礎的。 這意味着在輸入函數體的時刻,大部分工作已經由other構造完成(如果可能的話,通過移動構造函數,否則通過復制構造函數)。 因此,這會自動正確地實現復制和移動分配。

如果這是正確的,為什么(根據這個文檔至少std::vector 沒有以這種方式實現?


編輯以解釋這是如何工作的。 考慮以下示例中上述代碼中的other內容會發生什么

void foo(std::vector<bar> &&x)
{
  auto y=x;             // other is copy constructed
  auto z=std::move(x);  // other is move constructed, no copy is ever made.
  // ...
}

如果元素類型不是可復制的,或者容器不遵守強異常保證,則在目標對象具有足夠容量的情況下,復制賦值運算符可以避免分配:

vector& operator=(vector const& src)
{
    clear();
    reserve(src.size());  // no allocation if capacity() >= src.size()
    uninitialized_copy_n(src.data(), src.size(), dst.data());
    m_size = src.size();
}

實際上定義了三個賦值運算符:

vector& operator=( const vector& other );
vector& operator=( vector&& other );
vector& operator=( std::initializer_list<T> ilist );

您的建議vector& vector::operator=(vector other)使用復制和交換習慣用法。 這意味着,當調用操作符時,原始矢量將被復制到參數中,復制矢量中的每個項目。 然后這個副本將與this交換。 編譯器可能能夠忽略該副本,但該副本省略是可選的,移動語義是標准的。

您可以使用該習慣用法替換復制賦值運算符:

vector& operator=( const vector& other ) {
    swap(vector{other}); // create temporary copy and swap
    return *this;
}

每當復制任何元素拋出時,此函數也會拋出。

要實現移動賦值運算符,請忽略復制:

vector& operator=( vector&& other ) {
    swap(other);
    return *this;
}

由於swap()永遠不會拋出,因此移動賦值運算符也不會拋出。

通過使用移動賦值運算符和匿名臨時initializer_list也可以輕松實現initializer_list -assignment:

vector& operator=( std::initializer_list<T> ilist ) {
    return *this = vector{ilist};
}

我們使用了移動賦值運算符。 作為一個概念, initializer_list賦值operatpr只會在其中一個元素實例化時拋出。

正如我所說,編譯器可能能夠刪除副本以進行復制分配。 但編譯器沒有義務實現該優化。 它有義務實現移動語義。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM