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]);
这会使它更安全吗?
看起来像是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)需要正常工作,因为标准没有允许它不工作。
是的,它是安全的,并且标准库的实现会尽力确保其安全。
我相信实现者将这个要求追溯到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())
,它是否也适用? - Matthieu M.vector.push_back
确实有另外的规定。"如果新大小大于旧容量,则导致重新分配."并且(在 reserve
函数中)"重新分配会使所有引用、指针和迭代器都失效,这些引用、指针和迭代器指向序列中的元素." - Ben VoigtX
表示序列容器类,a
表示包含类型为T
的元素的X
的值,t
表示X::value_type
的左值或常数右值a.push_back(t)
返回类型 void
操作语义 追加t
的副本。 要求:T
必须能够CopyInsertable
到X
中。 容器 basic_string
、deque
、list
、vector
所以即使它并不是完全琐碎的,实现也必须保证在执行push_back
时不会使引用无效。std::for_each
еҝ…йЎ»еңЁиҢғеӣҙзҡ„еҮҪж•°еҜ№иұЎдҪҝз»“жқҹиҝӯд»ЈеҷЁж— ж•Ҳж—¶д»Қ然иғҪеӨҹе·ҘдҪңпјҹ е®ғеҰӮдҪ•еҸ‘зҺ°ж–°зҡ„жңүж•Ҳз»“жқҹиҝӯд»ЈеҷЁпјҹ еңЁи°ғз”Ёжңҹй—ҙпјҢжӮЁеҝ…йЎ»зЎ®дҝқж»Ўи¶іеүҚжҸҗжқЎд»¶гҖӮ - Ben Voigtfor_each
的函数对象要求不使迭代器失效。我找不到关于for_each
的参考资料,但在一些算法文本中看到类似“op和binary_op不应使迭代器或子���围失效”的字眼。 - bames53第一个示例看起来并不安全,因为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
中完成的,在这种情况下,此实现确实是安全的。 - Ben Voigt__swap_out_circular_buffer
内部。(我已经添加了一些注释来说明这一点。) - Nate Kohlpush_back
的多个副作用之间存在任何排序关系,即引用失效似乎未被定义为相对于复制构造新尾元素有序。v[0]
的结果不是一个迭代器,同样地,push_back()
也不接受一个迭代器。所以,从语言法律的角度来看,你的论点是无效的。抱歉。我知道大多数迭代器都是指针,并且使迭代器失效的原因和使引用失效的原因基本相同,但你引用的标准部分与现在的情况无关。 - cmaster - reinstate monicax.push_back(x[0])
是安全的。 - Nawaz它是完全安全的。
在您的第二个示例中,您有
v.reserve(v.size() + 1);
这并不是必要的,因为如果向量超出其大小,它将暗示使用reserve
。
向量负责这些事情,而不是你。
两者都是安全的,因为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
会 调整 向量的大小,在一个简单的实现中,这将在复制发生之前 使引用无效。因此,除非您可以从标准中支持此操作,否则我认为它是错误的。 - Konrad Rudolphpush_back
将值复制到向量中;但是(就我所看到的而言),这可能发生在重新分配之后,此时它试图复制的引用已不再有效。 - Mike Seymour来自23.3.6.5/1:如果新大小大于旧容量,则会导致重新分配。如果没有重新分配,插入点之前的所有迭代器和引用仍然有效。
由于我们是在末尾插入,如果向量未调整大小,则不会使任何引用无效。因此,如果向量的capacity() > size()
,则保证可以工作;否则,保证是未定义的行为。
push_back
的语义)。 - Angew is no longer proud of SO
push_back
的示例实现作为其中一部分。另一位发帖者指出了其中的一个错误,即它没有正确处理你所描述的情况。据我所知,没有其他人认为这不是一个错误。并不是说这是最终证明,只是一个观察结果。 - Benjamin Lindley