我正在尝试插入现有vector
元素的副本以使其加倍。以下代码在以前的版本中有效,但在Visual Studio 2010中失败。
#include <iostream>
#include <vector>
using namespace std;
int main(int argc, char* argv[])
{
vector<int> test;
test.push_back(1);
test.push_back(2);
test.insert(test.begin(), test[0]);
cout << test[0] << " " << test[1] << " " << test[2] << endl;
return 0;
}
输出为-17891602 1 2
,预计为1 1 2
。
我已经弄清楚它为什么会发生 - 向量被重新分配,并且引用在复制到插入点之前变为无效。较旧的Visual Studio显然以不同的顺序执行操作,从而证明未定义行为的一个可能结果是正确工作并且证明它永远不是您应该依赖的东西。
我想出了两种不同的方法来解决这个问题。一种是使用reserve
来确保不会发生重新分配:
test.reserve(test.size() + 1);
test.insert(test.begin(), test[0]);
另一种是从引用中复制,以便不依赖于引用的剩余有效:
template<typename T>
T make_copy(const T & original)
{
return original;
}
test.insert(test.begin(), make_copy(test[0]));
尽管两者都有效,但两者都不是一种天生的解决方案。有什么我想念的吗?
答案 0 :(得分:4)
问题是vector::insert
将对值的引用作为第二个参数而不是值。您不需要模板来制作副本,只需使用复制构造函数来创建另一个对象,该对象将通过引用传递。即使调整了矢量大小,此副本仍然有效。
#include <iostream>
#include <vector>
using namespace std;
int main(int argc, char* argv[])
{
vector<int> test;
test.push_back(1);
test.push_back(2);
test.insert(test.begin(), int(test[0]));
cout << test[0] << " " << test[1] << " " << test[2] << endl;
return 0;
}
答案 1 :(得分:1)
我相信这是定义的行为。在2011 C ++标准的§23.2.3
中,表100列出了序列容器的要求,并且这个案例有一个条目。它给出了示例表达式
a.insert(p,t)
其中a
是X
的值,它是包含T
类型元素的序列容器类型,p
是a
的常量迭代器, t
是X::value_type
类型的左值或常量值,即T
。
此表达式的断言是:
要求:
T
CopyInsertable
为X
。对于vector
和deque
,T
也应为CopyAssignable
。
效果:在t
之前插入p
的副本。
我能找到的唯一相关矢量特定报价位于§23.3.6.5
第1段:
备注:如果新大小大于旧容量,则会导致重新分配。如果没有重新分配,插入点之前的所有迭代器和引用仍然有效。
虽然这确实提到了重新分配的向量,但它并没有对序列容器上insert
的先前要求做出例外。
至于解决这个问题,我同意@ EdChum的建议,即只复制该元素并插入该副本。