2012-04-18 60 views
11

我试图插入现有的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

BTW vc11 dev预览给出了第一个例子中的'1 1 2'。 – 2012-04-18 21:28:54

+0

@Jesse,这并不让我感到意外。正在选择'insert'的Rvalue重载,这看起来像是他们可能已经修复的错误。该代码在超载和采用const引用的代码之间完全不同。 – 2012-04-18 21:32:24

+0

是否投射到int工作? – 2012-04-18 21:35:50

回答

1

我相信这是定义的行为。在2011年C++标准的§23.2.3中,表100列出了序列容器要求,并且存在这种情况的条目。它给出了示例表达

a.insert(p,t) 

其中aX一个值,它是含有T类型的元素的序列容器类型,p是一个常量迭代到a,和tX::value_type类型的左值或const右值,即T

此表达的断言是:

需要:TCopyInsertableX。对于vectordeque,T也应该是CopyAssignable
影响:p之前插入t的副本。

唯一相关的载体具体报价,我能找到在§23.3.6.5第1款:

注:导致重新分配,如果新的尺寸比旧款更大的容量。如果没有重新分配,插入点之前的所有迭代器和引用仍然有效。

虽然这确实提到了重新分配的向量,但它并不是例外,以前的需求序列容器上的insert

至于解决这个问题,我同意@ EdChum的建议只是制作一个元素的副本并插入该副本。

+0

我在你的描述中没有看到任何东西在调用't'是对'a'成员的引用。 – 2012-04-19 01:52:35

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; 
} 
+0

这只是一个测试案例。我的实际代码包含比int更复杂的元素,而临时构造函数只会变得很难看。好的建议,但。 – 2012-04-20 04:24:03