使用 placement new 作为复制赋值运算符不好吗?
Is using a placement new as a copy assignment operator bad?
有时我想与 const
成员建立 classes/structs。我意识到出于多种原因这是一个坏主意,但为了争论起见,让我们假装唯一的原因是至少可以说它使格式良好的 operator =
变得麻烦。但是,我设计了一个相当简单的解决方法,如以下结构所示:
struct S {
const int i;
S(int i) : i(i) {}
S(const S& other) : i(other.i) {}
S& operator =(const S& other) {
new (this) S(other);
return *this;
}
};
忽略析构函数和移动语义,有什么不应该这样做的真正重要原因吗?在我看来,它更像是
的类型安全版本
S& operator =(const S& other) {
const_cast<int&>(i) = other.i;
return *this;
}
所以,问题的总结是这样的:是否有任何主要原因不应该使用 placement-new 来实现复制赋值以具有与复制构造相同的语义?
我认为 placement new 在这里不是问题,但是 const_cast
会产生未定义的行为:
C++ 10.1.7.1-4
Except that any class member declared mutable (10.1.1) can be modified, any attempt to modify a const object during its lifetime (6.6.3) results in undefined behavior.
在编译器开始优化之前,您可能不会遇到这种情况。
另一个问题是在被活动 (non-destroyed) 对象占用的一块内存上使用了新的放置。但是当有问题的对象有一个微不足道的析构函数时,你可能会摆脱这个。
is there any really big reason why this shouldn't be done?
您必须绝对确定每个派生的 class 都定义了自己的赋值运算符,即使它是微不足道的。因为派生的 class 的 隐式定义 copy-assignment 运算符会搞砸 一切。它将调用 S::operator=
,这将 re-create 错误类型的对象 取而代之。
这样的 destroy-and-construct 赋值运算符 不能被任何派生的 class re-used。因此,您不仅强迫派生的 classes 提供显式复制运算符,而且还强迫它们在赋值运算符中坚持使用相同的 destroy-and-construct 习语。
您必须绝对确定没有其他线程正在访问该对象,而它正被这样的赋值运算符destroyed-and-constructed访问。
A class 可能有一些数据 成员不能受赋值运算符 的影响。例如,thread-safe class 可能有某种互斥体或关键部分成员,当当前线程要 destroy-and-construct 那个互斥体时,其他线程正在等待它们...
Performance-wise,与标准的 copy-and-swap 成语相比,它实际上 没有优势 。那么,经历了上述所有的痛苦,会有什么收获呢?
有时我想与 const
成员建立 classes/structs。我意识到出于多种原因这是一个坏主意,但为了争论起见,让我们假装唯一的原因是至少可以说它使格式良好的 operator =
变得麻烦。但是,我设计了一个相当简单的解决方法,如以下结构所示:
struct S {
const int i;
S(int i) : i(i) {}
S(const S& other) : i(other.i) {}
S& operator =(const S& other) {
new (this) S(other);
return *this;
}
};
忽略析构函数和移动语义,有什么不应该这样做的真正重要原因吗?在我看来,它更像是
的类型安全版本S& operator =(const S& other) {
const_cast<int&>(i) = other.i;
return *this;
}
所以,问题的总结是这样的:是否有任何主要原因不应该使用 placement-new 来实现复制赋值以具有与复制构造相同的语义?
我认为 placement new 在这里不是问题,但是 const_cast
会产生未定义的行为:
C++ 10.1.7.1-4 Except that any class member declared mutable (10.1.1) can be modified, any attempt to modify a const object during its lifetime (6.6.3) results in undefined behavior.
在编译器开始优化之前,您可能不会遇到这种情况。
另一个问题是在被活动 (non-destroyed) 对象占用的一块内存上使用了新的放置。但是当有问题的对象有一个微不足道的析构函数时,你可能会摆脱这个。
is there any really big reason why this shouldn't be done?
您必须绝对确定每个派生的 class 都定义了自己的赋值运算符,即使它是微不足道的。因为派生的 class 的 隐式定义 copy-assignment 运算符会搞砸 一切。它将调用
S::operator=
,这将 re-create 错误类型的对象 取而代之。这样的 destroy-and-construct 赋值运算符 不能被任何派生的 class re-used。因此,您不仅强迫派生的 classes 提供显式复制运算符,而且还强迫它们在赋值运算符中坚持使用相同的 destroy-and-construct 习语。
您必须绝对确定没有其他线程正在访问该对象,而它正被这样的赋值运算符destroyed-and-constructed访问。
A class 可能有一些数据 成员不能受赋值运算符 的影响。例如,thread-safe class 可能有某种互斥体或关键部分成员,当当前线程要 destroy-and-construct 那个互斥体时,其他线程正在等待它们...
Performance-wise,与标准的 copy-and-swap 成语相比,它实际上 没有优势 。那么,经历了上述所有的痛苦,会有什么收获呢?