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]);
这样安全吗?
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]);
这样安全吗?
Answers:
看起来http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-closed.html#526解决了该问题(或与之非常相似的问题),并将其作为标准中的潜在缺陷:
1)在执行函数期间可以更改由const引用获取的参数
例子:
给定std :: vector v:
v.insert(v.begin(),v [2]);
v [2]可以通过移动向量的元素来更改
提议的解决方案是,这不是缺陷:
必须使用vector :: insert(iter,value),因为该标准未授予其无效的许可。
v.insert(v.begin(), v[2]);
无法触发重新分配。那么这如何回答这个问题呢?
是的,它很安全,标准库实现也是如此。
我相信实施者可以某种方式将此要求追溯到23.2 / 11,但是我不知道如何做到,也找不到更具体的东西。我能找到的最好的是这篇文章:
http://www.drdobbs.com/cpp/copying-container-elements-from-the-c-li/240155771
对libc ++和libstdc ++实现的检查表明它们也是安全的。
vec.insert(vec.end(), vec.begin(), vec.end());
吗?
vector.push_back
另有规定。“如果新大小大于旧容量,则会导致重新分配。” 和(at reserve
)“重新分配会使引用序列中元素的所有引用,指针和迭代器无效。”
该标准甚至保证您的第一个示例都是安全的。引用C ++ 11
[sequence.reqmts]
3在表100和表101中...
X
表示序列容器类,a
表示X
包含类型的元素的值T
,...t
表示以下项的左值或常量值:X::value_type
16表101 ...
表达式
a.push_back(t)
返回类型void
操作语义追加的副本t.
要求:T
应CopyInsertable
成X
。 集装箱basic_string
,deque
,list
,vector
因此,即使它并非完全无关紧要,实现也必须确保在执行时不会使引用无效push_back
。
t
,唯一的问题是在复制之前还是之后。您的最后一句话肯定是错误的。
t
满足列出的前提条件,因此可以保证上述行为。不允许实现使前提条件无效,然后以该前提条件为借口以不按指定的方式行事。
for_each
不能使迭代器无效。我无法提供的参考for_each
,但我在某些算法上看到“ op和binary_op不会使迭代器或子范围无效”的文字。
显然第一个示例是安全的,因为的最简单实现是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.
}
__swap_out_circular_buffer
中完成的,在这种情况下,此实现确实是安全的。
__swap_out_circular_buffer
。(我要添加一些注释来说明这一点。)
第一个版本绝对不安全:
通过调用标准库容器或字符串成员函数获得的迭代器上的操作可以访问基础容器,但不得对其进行修改。[注意:特别是,使迭代器无效的容器操作与对该容器关联的迭代器上的操作发生冲突。—尾注]
从第17.6.5.9节开始
请注意,这是关于数据竞争的部分,人们通常将其与线程结合使用……但是实际定义涉及“先于发生”的关系,并且我看不到push_back
in 的多个副作用之间的任何排序关系。在这里玩,也就是说,参考无效似乎没有定义为相对于复制构造新尾元素的顺序。
v[0]
不是迭代器,同样,push_back()
不采用迭代器。因此,从语言律师的角度来看,您的论点是无效的。抱歉。我知道,大多数迭代器都是指针,并且使迭代器无效的要点与引用几乎相同,但是您引用的标准部分与当前情况无关。
x.push_back(x[0])
是安全的。
两者都是安全的,因为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]
因此违反了规范。
push_back
但是,它也会调整向量的大小,并且在幼稚的实现中,这将在复制发生之前使引用无效。因此,除非您可以通过引用标准来对此进行支持,否则我将认为它是错误的。
push_back
将值复制到向量中;但是(据我所知)重新分配后可能会发生,这时它尝试从中复制的引用不再有效。
push_back
通过引用接受其论点。
从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()
则可以保证能正常工作,否则,它将保证是未定义的行为。
references
对引号的部分感兴趣。
push_back
)。
push_back
。另一位发帖人指出其中存在一个错误,即它无法正确处理您描述的情况。据我所知,没有人认为这不是错误。并不是说那是确凿的证据,只是一个观察。