【问题标题】:libc++ difference between vector::insert overloadslibc++ vector::insert 重载的区别
【发布时间】:2015-08-22 03:10:39
【问题描述】:

libc++的std::vectorimplementation有以下insert的重载:

template <class _Tp, class _Allocator>
typename vector<_Tp, _Allocator>::iterator
vector<_Tp, _Allocator>::insert(const_iterator __position, const_reference __x)
{
    pointer __p = this->__begin_ + (__position - begin());
    if (this->__end_ < this->__end_cap())
    {
        __RAII_IncreaseAnnotator __annotator(*this);
        if (__p == this->__end_)
        {
            __alloc_traits::construct(this->__alloc(),
                                      _VSTD::__to_raw_pointer(this->__end_), __x);
            ++this->__end_;
        }
        else
        {
            __move_range(__p, this->__end_, __p + 1);
            const_pointer __xr = pointer_traits<const_pointer>::pointer_to(__x);
            if (__p <= __xr && __xr < this->__end_) // [*]
                ++__xr;
            *__p = *__xr;
        }
        __annotator.__done();
    }
    else
    {
        allocator_type& __a = this->__alloc();
        __split_buffer<value_type, allocator_type&> __v(__recommend(size() + 1), __p - this->__begin_, __a);
        __v.push_back(__x);
        __p = __swap_out_circular_buffer(__v, __p);
    }
    return __make_iter(__p);
}

... 和类似的,采用右值引用:

template <class _Tp, class _Allocator>
typename vector<_Tp, _Allocator>::iterator
vector<_Tp, _Allocator>::insert(const_iterator __position, value_type&& __x)
{
    pointer __p = this->__begin_ + (__position - begin());
    if (this->__end_ < this->__end_cap())
    {
        __RAII_IncreaseAnnotator __annotator(*this);
        if (__p == this->__end_)
        {
            __alloc_traits::construct(this->__alloc(),
                                      _VSTD::__to_raw_pointer(this->__end_),
                                      _VSTD::move(__x));
            ++this->__end_;
        }
        else
        {
            __move_range(__p, this->__end_, __p + 1);
            *__p = _VSTD::move(__x);
        }
        __annotator.__done();
    }
    else
    {
        allocator_type& __a = this->__alloc();
        __split_buffer<value_type, allocator_type&> __v(__recommend(size() + 1), __p - this->__begin_, __a);
        __v.push_back(_VSTD::move(__x));
        __p = __swap_out_circular_buffer(__v, __p);
    }
    return __make_iter(__p);
}

第一次重载中标记[*]的分支的目的是什么?是标准要求的吗?为什么在第二次重载中没有它?我在 libstdc++ 中找不到等效的构造。

编辑libstdc++solves通过创建临时副本来解决同样的问题。

【问题讨论】:

  • 谢谢,现在看起来不错:-)

标签: c++ c++11 vector libc++


【解决方案1】:

这是处理您尝试插入的元素已存在于vector 中的情况的条件。

为了解释这一点,让我们从定义函数中使用的变量开始。

  • __p 是指向要插入新元素的位置的指针
  • __xr 是指向要插入的元素地址的指针

vector 有足够的容量插入一个额外的元素(if (this-&gt;__end_ &lt; this-&gt;__end_cap()))时,您询问的代码路径将被执行。此外,插入点不是 end() 迭代器(if (__p == this-&gt;__end_) — 执行的是 else 路径)。

在这种情况下,实现首先将 [__p, end()) 范围内的所有内容移动一个位置 — __move_range(__p, this-&gt;__end_, __p + 1);

但是,如果您尝试插入的元素是刚刚被移动的范围的一部分呢?如果是这样,您必须增加指向要插入的元素的指针。这就是以下几行的作用

if (__p <= __xr && __xr < this->__end_)
  ++__xr;

右值引用重载没有做同样的检查,因为允许实现假设右值引用所引用的任何对象都是唯一引用的,因此尝试使用对一个元素的右值引用执行insert vector 中已经存在是未定义的行为。

来自 N3337,§17.6.4.9/1 [res.on.arguments]

除非另有明确说明,否则以下各项均适用于 C++ 标准库中定义的函数的所有参数。
— ...
— 如果函数参数绑定到右值引用参数,则实现可能假定此参数是对该参数的唯一引用。

这是defect report 和上述条款的基本原理。

【讨论】:

  • 很好的答案!我错过了可以插入已经插入的东西的事实。
  • @erenon 我记得在某处读过一个讨论,这可能是由于遗漏。标准中没有任何内容告诉您不允许插入已经在同一向量中的元素,因此需要实现来支持这种可能性。
  • +1,很好的解释。我故意违反评论这个upvote的规则,因为我编写了受到质疑的代码,以及标准中支持实现的文字。
猜你喜欢
  • 1970-01-01
  • 2016-09-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-31
  • 1970-01-01
  • 2013-01-25
相关资源
最近更新 更多