在 STL 实现中放置新的 vs 赋值

Placement new vs assignment in STL implementation

我一直好奇地检查 STL 的实现,并在 experimental/optional 中找到了这段代码。这是代码:

optional&
operator=(const optional& __opt)
{
    if (this->__engaged_ == __opt.__engaged_)
    {
        if (this->__engaged_)
            this->__val_ = __opt.__val_;
    }
    else
    {
        if (this->__engaged_)
            this->__val_.~value_type();
        else
            ::new(_VSTD::addressof(this->__val_)) value_type(__opt.__val_);
        this->__engaged_ = __opt.__engaged_;
    }
    return *this;
}

这是 std::optional<T> class 的复制赋值运算符实现。我相信为了讨论的效果,弄清楚这些变量是什么也很重要,所以这里是 classe 的存储:

typedef _Tp value_type;
union
{
    char __null_state_;
    value_type __val_;
};
bool __engaged_ = false;

第一个代码摘录以两种不同的方式显示对 __val_ 的赋值,一种使用简单赋值 (this->__val_ = __opt.__val_),另一种使用新放置 (::new(_VSTD::addressof(this->__val_)) value_type(__opt.__val_);)。有什么不同?在这种情况下,为什么要使用一个或另一个?

默认情况下不初始化可选中的存储。在第一种情况下,存储已经构建,因此它使用对象的复制赋值运算符。

在第二种情况下,它需要将一个对象构造到分配的存储空间中,因为在该代码路径中,赋值的左侧从未构造过它的存储空间..

这或多或少是唯一的方法。当rhs(__opt)不是'engaged'时,需要去析构__val__,如果是engaged,而我们的member不是,我们就得在那里创建一个对象。

第一个 if 处理两个参数都是 'engaged' 或不是 'engaged' 的情况,在这种情况下,要么什么都不做,要么我们可以使用简单的赋值.