据我所知,C ++编译器生成如下的赋值运算符:
struct X {
std::vector<int> member1;
std::vector<int> member2;
X& operator=(const X& other) {
member1 = other.member1;
member2 = other.member2;
}
};
这个例外不安全吗?如果member2 = other.member2
抛出,则原始作业的副作用不会被撤消。
答案 0 :(得分:8)
如果对象的每个成员提供基本异常保证或“更好”,并且对象不变量没有成员内依赖性,则编译器生成的赋值运算符具有基本异常保证。
如果每个成员的任务也具有无投保证,则无投保。
很少(如果有的话)有强有力的保证,这可能就是你所说的“异常不安全”。
复制交换习惯用法很受欢迎,因为编写无掷swap
通常很容易,而构造函数应该已经提供强大的异常保证。结果是operator=
具有强大的异常保证。 (在move
的情况下,它是伪强的,因为输入通常不会回滚,但它是一个右值)
void swap( Foo& other ) noexcept; // implement this
Foo& operator=( Foo const& f ) {
Foo tmp(f);
swap( tmp );
return *this;
}
Foo& operator=( Foo && f ) {
Foo tmp(std::move(f));
swap( tmp );
return *this;
}
如果您也采用副值按值,有时可以将operator=
升级为无抛出(唯一的例外是可能在构造参数时)。
Foo& operator=( Foo f ) noexcept {
swap( f );
return *this;
}
在某些情况下,Foo
的某些构造函数是noexcept
而其他构造函数不是:通过取另一个by-value,我们提供了总体上最好的异常保证(作为{=
的参数{1}}有时可以通过省略或通过{}
直接构建来直接构建。
由于某些原因,该语言(至少在此时)实施具有强保证的复制交换operator=
是不切实际的。首先,C ++运行“你只需支付你使用的费用”,而copy-swap可能比成员副本更昂贵。其次,swap
目前不是核心语言的一部分(有一些建议要添加operator :=:
并将其折叠起来)。第三,与以前版本的C ++和C。
答案 1 :(得分:1)
2014年6月4日编辑
我最初的答案是基于我的理解,即原始海报寻求一个任务操作员,保证不会抛出异常。根据各种评论者的说法,只要对象在异常时保持不变,很明显异常就可以了。
建议的方法是通过临时变量和std :: swap()。
X& X::operator=(const X& other)
{
// assign to temps. If this throws, the object
// has not changed.
auto m1 = other.member1;
auto m2 = other.member2;
// the theory is, that swap won't throw
// can we rely on that?
std::swap(m1, member1);
std::swap(m2, member2);
return *this;
}
实际上我们不能依赖swap()不会抛出异常,除非我们对我们对象的组件有所了解。
为了确保swap()永远不会抛出,我们需要成员对象为Move Assignable和Move Constructible。
在给出的示例中,使用C ++ 11或更高版本的编译器,std :: vector&lt; int&gt; 是 移动可分配和移动可构造,所以我们是安全的。然而,作为一般解决方案,我们始终需要了解我们正在做出的任何假设,并检查它们是否正在持有。