c++统一赋值操作符移动语义
C++ Unified Assignment Operator move-semantics
EDIT: solved见评论——没有答案,不知道如何标记为解决。
在看了第9频道关于c++0x中的完美转发/移动语义的视频后,我相信这是编写新的赋值操作符的好方法。
#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&S赋值给它。通过使用适当的构造函数构造一个新对象,然后用*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;
}
现在您的客户端可以选择效率(my type::operator=)或使用strong_assign
的强异常安全性。
你仔细阅读错误信息了吗?它发现了两个错误,即有多个复制赋值操作符和多个移动赋值操作符。这完全正确!
特殊成员必须最多指定一次,无论它们是默认的、已删除的、按惯例定义的,还是通过省略来隐式处理的。您有两个复制赋值操作符(一个取my_type
,另一个取my_type const &
)和两个移动赋值操作符(一个取my_type
,另一个取my_type &&
)。注意,以my_type
为参数的赋值操作符可以处理左值和右值引用,因此它既充当复制赋值操作,也充当移动赋值操作。
大多数特殊成员的函数签名都有多种形式。你必须选一个;你不能使用一个不寻常的,然后删除常规的,因为那将是一个双重声明。编译器将自动使用不寻常的特殊成员,而不会用常规签名合成特殊成员。
(注意,错误中提到了三个候选者。对于每个赋值类型,它会看到适当的已删除方法,即接受my_type
的方法,然后另一个已删除方法作为紧急近匹配。)
您是否应该删除这些赋值操作符的重载?赋值操作符的声明不应该是模板之类的吗?我真不明白这是怎么回事。
请注意,即使这样做,通过实现move赋值操作符,刚刚被移动的对象所持有的资源将在其生命周期结束时释放,而不是在赋值时释放。查看更多详细信息:
http://cpp-next.com/archive/2009/09/your-next-assignment/- 何时在引用或唯一指针上使用移动语义
- 如何从具有移动语义的类对象中生成共享指针
- 可以使用移动语义更改或改进此C++代码吗?
- c++在使用指针时移动语义
- 移动语义和深层/浅层复制之间有什么关系?
- std::unique_lock移动语义
- 移动语义和运算符 + 重载
- C++ 移动语义是否在任何情况下都能节省资源?
- 移动语义在这里如何工作?
- 使用移动语义:右值引用作为方法参数
- 在C++中使用移动语义的正确方法是什么?
- 移动语义 c++ 单链表
- C++:使用整数移动语义
- 当变量和参数名称匹配时,移动语义构造失败
- 在 C++11 中移动语义
- 方法冗余移动调用的移动语义
- 复制省略并在返回值中移动语义
- std::元组和移动语义
- 移动语义与返回shared_ptr?
- C++具有移动语义的可变参数工厂会导致运行时崩溃