从同一向量push_back元素是否安全
Is it safe to push_back an element from the same vector?
vector<int> v;
v.push_back(1);
v.push_back(v[0]);
如果第二个push_back导致重新分配,则对向量中第一个整数的引用将不再有效。所以这不安全吗?
vector<int> v;
v.push_back(1);
v.reserve(v.size() + 1);
v.push_back(v[0]);
这样安全吗?
看起来 http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-closed.html#526 解决了这个问题(或与之非常相似的东西)作为标准中的潜在缺陷:
1) 常量引用的参数可以在执行过程中更改 的功能
例子:
给定 std::vector v:
v.insert(v.begin(), v[2]);
v[2] 可以通过移动矢量元素来改变
拟议的决议是,这不是一个缺陷:
向量::插入(迭代,值)需要工作,因为标准 不允许它不起作用。
是的,它是安全的,标准库实现会跳过箍来做到这一点。
我相信实施者以某种方式将这一要求追溯到 23.2/11,但我无法弄清楚如何,我也找不到更具体的东西。我能找到的最好的是这篇文章:
http://www.drdobbs.com/cpp/copying-container-elements-from-the-c-li/240155771
对libc++和libstdc++的实现的检查表明它们也是安全的。
该标准甚至保证您的第一个示例是安全的。引用 C++11
[序列.reqmts]
3 在表 100 和 101 中 ...
X
表示序列容器类,a
表示包含类型为T
的元素的X
的值,...t
表示左值或常量右值X::value_type
16 表 101 ...
表达式
a.push_back(t)
返回类型void
操作语义 附加t.
的副本 要求:T
应CopyInsertable
X
中。 容器basic_string
、deque
、list
、vector
因此,即使它不是微不足道的,实现也必须保证在执行push_back
时不会使引用无效。
第一个示例是否安全并不明显,因为push_back
最简单的实现是首先重新分配向量(如果需要),然后复制引用。
但至少在Visual Studio 2010中它似乎是安全的。它的 push_back
实现会在您推回向量中的元素时对情况进行特殊处理。代码结构如下:
void push_back(const _Ty& _Val)
{ // insert element at end
if (_Inside(_STD addressof(_Val)))
{ // push back an element
...
}
else
{ // push back a non-element
...
}
}
标准的保证,但作为另一个数据点,v.push_back(v[0])
对于LLVM的libc ++是安全的。
libc++ 的std::vector::push_back
调用__push_back_slow_path
当它需要重新分配内存时:
void __push_back_slow_path(_Up& __x) {
allocator_type& __a = this->__alloc();
__split_buffer<value_type, allocator_type&> __v(__recommend(size() + 1),
size(),
__a);
// Note that we construct a copy of __x before deallocating
// the existing storage or moving existing elements.
__alloc_traits::construct(__a,
_VSTD::__to_raw_pointer(__v.__end_),
_VSTD::forward<_Up>(__x));
__v.__end_++;
// Moving existing elements happens here:
__swap_out_circular_buffer(__v);
// When __v goes out of scope, __x will be invalid.
}
第一个版本绝对不安全:
通过调用标准库容器或字符串成员函数获得的迭代器上的操作可以访问底层容器,但不得修改它。[ 注意:特别是,使迭代器失效的容器操作与与该容器关联的迭代器上的操作冲突。
来自第 17.6.5.9 节
请注意,这是关于数据竞赛的部分,人们通常会将其与线程结合使用......但实际定义涉及"发生在之前"的关系,我在这里没有看到push_back
的多重副作用之间的任何排序关系,即引用无效似乎没有被定义为关于复制构造新尾元素的有序。
它是完全安全的。
在您的第二个示例中,您有
v.reserve(v.size() + 1);
这是不需要的,因为如果矢量超出其大小,它将意味着reserve
.
Vector负责这些东西,而不是你。
两者都是安全的,因为push_back会复制值,而不是引用。如果你存储指针,就向量而言,这仍然是安全的,但只要知道你的向量有两个元素指向相同的数据。
部分 23.2.1 一般容器要求
16
- a.push_back(t) 附加 t 的副本。 要求:T 应可复制插入到 X 中。
- a.push_back(rv) 追加 rv 的副本。 要求:T 应可移动到 X 中。
因此,push_back的实现必须确保插入v[0]
的副本。通过反例,假设一个在复制之前重新分配的实现,它不会肯定会附加v[0]
的副本,因此违反了规范。
来自 23.3.6.5/1:Causes reallocation if the new size is greater than the old capacity. If no reallocation happens, all the iterators and references before the insertion point remain valid.
由于我们在末尾插入,因此如果未调整矢量的大小,则不会使引用失效。因此,如果向量capacity() > size()
那么它保证工作,否则保证它是未定义的行为。
- Mongodb c++驱动程序:如何查询元素的数组
- 将数组作为参数传递给函数安全吗?作为第三方职能部门,可以探索他们想要的之外的其他元素
- 使用strcpy将char数组的元素复制到另一个数组
- 使用不带参数的函数访问结构元素
- 给定n个元素的m个集合.在C++中找到出现在最大集合数中的元素
- C++如何通过用户输入删除列表元素
- lower_bound()返回最后一个元素
- 基于多个条件处理地图中的所有元素
- 调整大小后指向元素值的指针unordered_map有效?
- 使用std::transform将一个范围的元素添加到另一个范围中
- 使用函数"remove"删除重复元素
- 具有最大子序列大小的序列,每个元素都相同
- 如何将两个不同矢量的同一位置的两个元素组合在一起
- 如何将元素添加到数组的线程安全函数?
- 有没有办法将谓词中的元素偏移量传递给 std 算法?
- 我想访问std::unique_ptr中的一个特定元素
- 返回C++中没有 back() 方法的容器的最后一个元素?
- vector.back() 可以用来为向量的最后一个元素赋值吗?
- 删除链表中的 back 元素
- 在 C++ 中将元素添加到空向量:为什么 push.back 有效而 [] 无效