是否允许将std :: vector的元素插入到同一个向量中?

Cho*_*s-2 17 c++ stl vector stdvector c++11

考虑以下insertemplace成员函数std::vector<T>:

template <class... Args> iterator emplace(const_iterator position, Args&&... args);
iterator insert(const_iterator position, const T& x);
iterator insert(const_iterator position, T&& x);
iterator insert(const_iterator position, size_type n, const T& x);
Run Code Online (Sandbox Code Playgroud)

如果通过引用向量本身的元素作为参数来调用其中一个,该怎么办?通常,它们中的每一个都会使对所有元素的引用无效,这些元素position可能包括参数,或者如果发生重新分配,则引用所有元素,这肯定包含它,但这是否意味着这样的调用无效或插入(似乎先发生?

查看一些常见的实现会产生奇怪的结果:

  • libstdc ++在移动任何元素之前复制参数,但仅在const T&重载中insert.它包含这个评论:

    这三个操作的顺序由C++ 0x情况决定,其中移动可以改变属于现有向量的新元素.这只是调用者通过const lvalue ref获取元素的问题(见23.1/13).

    但是C++11§23.1只是容器库的简短摘要,即使我们假设这是指§23.2.1(以前是C++ 03中的§23.1),仅仅是§23.2.1/ 13给出了分配器感知容器的定义,这似乎与此无关.我已经看了第23章,但我没有找到任何相关的东西.

  • libc ++在移动任何元素之前创建一个临时元素emplace,而在insert其中首先移动元素但将参数引用转换为指针并调整它以确保它指向原始元素 - 但同样,它仅在const T&重载中完成所有这些操作.

  • 在所有情况下移动任何元素之前,Visual C++会创建一个副本/临时文件.

我错过了标准定义此行为的地方吗?为什么我看到的三个C++库彼此不一致?为什么libstdc ++评论说它只是一个问题insert(const_iterator, const T&)?如果标准不要求这个工作,为什么图书馆总是懒得让它工作呢?(当然这会花费一些本来可以避免的副本和/或移动.)最后,如果我正在实现一个类似的容器,std::vector我应该让它工作吗?

Rei*_*ica 8

首先回答第二个问题:标准明确表示允许标准库假设在通过rvalue引用传递某些内容时,该rvalue引用是对该对象的唯一引用.这意味着它不能合法地成为向量的元素.C++ 11 17.6.4.9/1的相关部分:

  • 如果函数参数绑定到右值引用参数,则实现可以假定此参数是对此参数的唯一引用.... [ 注意:如果程序在将左值传递给库函数时将左值转换为x值(例如通过使用参数调用该函数 move(x)),则程序有效地要求该函数将该左值作为临时值处理.实现可以自由地优化别名检查,如果参数是左值,则可能需要这些检查.- 尾注 ]

这让我们只是处理这个const T &案子.尽管libstdc ++和libc ++在这种情况下有所不同,但它们的净结果是相同的 - 它们将从传入的对象中正确复制.而标准只规定了行为,而不是实现.只要他们达到正确的行为,他们就没事了.

  • @ Chortos-2:他们**必须**,行为在标准中定义为在正确的位置插入原始对象的副本.对象是否可以对向量中的元素进行别名没有限制,因此实现需要确保即使在别名的情况下也遵循*required*行为.比较具有这个前提条件的`insert(p,i,j)`:*i和j不是迭代器变成.* - 这是BTW为什么你不应该尝试重新实现标准容器的另一个原因,很可能是实现不会那么好. (2认同)