[英]C++ move semantics: why copy assignment operator=(&) is called instead of move assignment operator=(&&)?
[英]C++ Unified Assignment Operator move-semantics
編輯:解決看到評論 - 不知道如何標記解決與答案。
在c ++ 0x中觀看有關完美轉發/移動語義的第9頻道視頻之后,我認為這是編寫新賦值運算符的好方法。
#include <string>
#include <vector>
#include <iostream>
struct my_type
{
my_type(std::string name_)
: name(name_)
{}
my_type(const my_type&)=default;
my_type(my_type&& other)
{
this->swap(other);
}
my_type &operator=(my_type other)
{
swap(other);
return *this;
}
void swap(my_type &other)
{
name.swap(other.name);
}
private:
std::string name;
void operator=(const my_type&)=delete;
void operator=(my_type&&)=delete;
};
int main()
{
my_type t("hello world");
my_type t1("foo bar");
t=t1;
t=std::move(t1);
}
這應該允許將r值和const分配給它。 通過使用適當的構造函數構造一個新對象,然后使用* this交換內容。 這對我來說聽起來很合理,因為沒有數據被復制超過它需要的數量。 指針算術很便宜。
但是我的編譯器不同意。 (g ++ 4.6)我得到了這些錯誤。
copyconsttest.cpp: In function ‘int main()’:
copyconsttest.cpp:40:4: error: ambiguous overload for ‘operator=’ in ‘t = t1’
copyconsttest.cpp:40:4: note: candidates are:
copyconsttest.cpp:18:11: note: my_type& my_type::operator=(my_type)
copyconsttest.cpp:30:11: note: my_type& my_type::operator=(const my_type&) <deleted>
copyconsttest.cpp:31:11: note: my_type& my_type::operator=(my_type&&) <near match>
copyconsttest.cpp:31:11: note: no known conversion for argument 1 from ‘my_type’ to ‘my_type&&’
copyconsttest.cpp:41:16: error: ambiguous overload for ‘operator=’ in ‘t = std::move [with _Tp = my_type&, typename std::remove_reference< <template-parameter-1-1> >::type = my_type]((* & t1))’
copyconsttest.cpp:41:16: note: candidates are:
copyconsttest.cpp:18:11: note: my_type& my_type::operator=(my_type)
copyconsttest.cpp:30:11: note: my_type& my_type::operator=(const my_type&) <deleted>
copyconsttest.cpp:31:11: note: my_type& my_type::operator=(my_type&&) <deleted>
難道我做錯了什么? 這是不好的做法(我認為沒有辦法測試你是否自我分配)? 編譯器還沒准備好嗎?
謝謝
對副本/交換分配習慣用語非常謹慎。 它可能是次優的,特別是在沒有仔細分析的情況下應用時。 即使您對賦值運算符需要強大的異常安全性,也可以以其他方式獲得該功能。
對於你的例子,我建議:
struct my_type
{
my_type(std::string name_)
: name(std::move(name_))
{}
void swap(my_type &other)
{
name.swap(other.name);
}
private:
std::string name;
};
這將獲得隱式復制和移動語義,這些語義轉發到std :: string的復制和移動成員。 std :: string的作者最了解如何完成這些操作。
如果您的編譯器尚不支持隱式移動生成,但支持默認的特殊成員,則可以執行以下操作:
struct my_type
{
my_type(std::string name_)
: name(std::move(name_))
{}
my_type(const mytype&) = default;
my_type& operator=(const mytype&) = default;
my_type(mytype&&) = default;
my_type& operator=(mytype&&) = default;
void swap(my_type &other)
{
name.swap(other.name);
}
private:
std::string name;
};
如果您只是想明確您的特殊成員,您也可以選擇執行上述操作。
如果您正在處理尚未支持默認特殊成員(或隱式移動成員)的編譯器,那么您可以顯式提供編譯器在完全符合C ++ 11時最終應該默認的內容:
struct my_type
{
my_type(std::string name_)
: name(std::move(name_))
{}
my_type(const mytype& other)
: name(other.name) {}
my_type& operator=(const mytype& other)
{
name = other.name;
return *this;
}
my_type(mytype&& other)
: name(std::move(other.name)) {}
my_type& operator=(mytype&& other)
{
name = std::move(other.name);
return *this;
}
void swap(my_type &other)
{
name.swap(other.name);
}
private:
std::string name;
};
如果你真的需要強大的異常安全性,可以設計一次並明確它(編輯包括Luc Danton的建議):
template <class C>
typename std::enable_if
<
std::is_nothrow_move_assignable<C>::value,
C&
>::type
strong_assign(C& c, C other)
{
c = std::move(other);
return c;
}
template <class C>
typename std::enable_if
<
!std::is_nothrow_move_assignable<C>::value,
C&
>::type
strong_assign(C& c, C other)
{
using std::swap;
static_assert(std::is_nothrow_swappable_v<C>, // C++17 only
"Not safe if you move other into this function");
swap(c, other);
return c;
}
現在,您的客戶可以使用strong_assign
在效率(我的類型::運算符=)或強異常安全性之間進行strong_assign
。
您是否仔細閱讀了錯誤消息? 它會看到兩個錯誤,即您有多個復制賦值運算符和多個移動賦值運算符。 這是完全正確的!
特殊成員必須最多指定一次,無論它們是否被默認,刪除,按常規定義,或通過被忽略而隱式處理。 你有兩個拷貝賦值運算符(一個采用my_type
,另一個采用my_type const &
)和兩個運動賦值運算符(一個采用my_type
,另一個采用my_type &&
)。 請注意,采用my_type
的賦值運算符可以處理左值和右值引用,因此它既可以作為復制賦值,也可以作為移動賦值。
大多數特殊成員的功能簽名有多種形式。 你必須選一個; 你不能使用一個不尋常的,然后刪除傳統的,因為這將是一個雙重聲明。 編譯器將自動使用異常形成的特殊成員,並且不會使用傳統簽名合成特殊成員。
(請注意,錯誤提到了三個候選。對於每個分配類型,它會看到相應的已刪除方法,采用my_type
的方法,然后將另一個已刪除的方法視為緊急匹配。)
你應該刪除賦值運算符的那些重載嗎? 您的賦值運算符聲明不應該是模板或其他東西嗎? 我真的不明白這是怎么回事。
請注意,即使這樣可行,通過以這種方式實現移動賦值運算符,剛剛移動的對象所擁有的資源將在其生命周期結束時釋放,而不是在賦值時釋放。 有關詳細信息,請參見此處
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.