Fre*_*rud 6 c++ assignment-operator move-semantics move-assignment-operator
我经常需要为“原始”资源句柄(例如文件句柄,Win32 OS句柄等)实现C ++包装。这样做时,我还需要实现move运算符,因为默认的编译器生成的操作符不会清除move-from对象,从而产生双删除问题。
在实现移动分配运算符时,我更喜欢显式调用析构函数,并使用new放置就地重新创建对象。这样,我避免了析构函数逻辑的重复。另外,我经常根据copy + move(在相关时)实现副本分配。这将导致以下代码:
/** Canonical move-assignment operator.
Assumes no const or reference members. */
TYPE& operator = (TYPE && other) noexcept {
if (&other == this)
return *this; // self-assign
static_assert(std::is_final<TYPE>::value, "class must be final");
static_assert(noexcept(this->~TYPE()), "dtor must be noexcept");
this->~TYPE();
static_assert(noexcept(TYPE(std::move(other))), "move-ctor must be noexcept");
new(this) TYPE(std::move(other));
return *this;
}
/** Canonical copy-assignment operator. */
TYPE& operator = (const TYPE& other) {
if (&other == this)
return *this; // self-assign
TYPE copy(other); // may throw
static_assert(noexcept(operator = (std::move(copy))), "move-assignment must be noexcept");
operator = (std::move(copy));
return *this;
}
Run Code Online (Sandbox Code Playgroud)
这让我感到很奇怪,但是我没有在网上看到任何有关以这种“规范”方式实现move + copy赋值运算符的建议。相反,大多数网站倾向于以一种特定于类型的方式来实现赋值运算符,在维护类时必须手动使其与构造函数和析构函数保持同步。
是否有任何反对(除了性能之外)以这种与类型无关的“规范”方式实现移动和复制分配运算符的论点?
我已经阅读了http://eel.is/c++draft/basic.life#8,该书似乎涵盖了所讨论的案例。提取:
如果在对象的生命周期结束后...,在原始对象占用的存储位置创建了一个新对象,则指向原始对象的指针,指向原始对象的引用...将自动引用新对象,并且,...可以用于操纵新对象,如果...
此后有一些明显的条件与相同的类型和const /引用成员有关,但似乎是任何赋值运算符实现所必需的。如果我错了,请纠正我,但是在我看来,我的“规范”样本行为良好,而不是 UB(?)
赋值实现可以合并为一个采用值参数而不是引用的方法。这似乎也消除了对static_assert和自赋值检查的需要。我的新提议实施将变为:
/** Canonical copy/move-assignment operator.
Assumes no const or reference members. */
TYPE& operator = (TYPE other) noexcept {
static_assert(!std::has_virtual_destructor<TYPE>::value, "dtor cannot be virtual");
this->~TYPE();
new(this) TYPE(std::move(other));
return *this;
}
Run Code Online (Sandbox Code Playgroud)