【问题标题】:How to insert a duplicate element into a vector?如何将重复元素插入向量中?
【发布时间】:2012-04-30 09:15:45
【问题描述】:

我正在尝试插入现有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]));

虽然两者都有效,但都不是一个自然的解决方案。我有什么遗漏吗?

【问题讨论】:

  • BTW vc11 dev preview 为第一个示例提供1 1 2
  • @Jesse,这并不让我感到惊讶。正在选择insert 的右值重载,这似乎是他们可能已修复的错误。该重载与采用 const 引用的代码完全不同。
  • @MarkRansom: 这个bug report 好像是它。
  • @DanNissenbaum,不,它没有。转换为 const int &amp; 有效,但我认为这只是因为它导致它使用旧代码路径。这不是一个正确的解决方法。
  • 我认为您的建议没有太多选择,您可以在插入之前取值,例如int copy = test[0]; test.insert(test.begin(), copy);

标签: c++ visual-studio-2010 vector


【解决方案1】:

问题在于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;
}

【讨论】:

  • 这只是一个测试用例。我的实际代码包含比 int 复杂得多的元素,并且临时的构造函数变得丑陋。不过建议很好。
【解决方案2】:

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

a.insert(p,t)

其中aX 的值,它是包含T 类型元素的序列容器类型,pa 的常量迭代器,t 是左值或常量右值类型为X::value_type,即T

这个表达式的断言是:

要求: T 应为 CopyInsertableX。对于vectordequeT 也应为CopyAssignable
效果:p 之前插入t 的副本。

我能找到的唯一相关矢量特定引用在§23.3.6.5 第 1 段中:

备注: 如果新容量大于旧容量,则会导致重新分配。如果没有发生重新分配,则插入点之前的所有迭代器和引用都保持有效。

虽然这确实提到了被重新分配的向量,但它并没有对序列容器上insert 的先前要求做出例外。

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

【讨论】:

  • 我在您的描述中没有看到任何提到 t 是对 a 成员的引用的情况。
猜你喜欢
  • 1970-01-01
  • 2010-12-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-26
  • 2021-02-10
  • 1970-01-01
相关资源
最近更新 更多