std::推回和插入之间的向量不一致崩溃(end(),x) 将此代码放入MS Visual C++ 2010中,编译(调试或发布),它会为插入()循环崩溃,但不是推送回退循环: #include <vector> #include <string> using std::vector; using std::string; int main() { vector<string> vec1; vec1.push_back("hello"); for (int i = 0; i != 10; ++i) vec1.push_back( vec1[0] ); vector<string> vec2; vec2.push_back("hello"); for (int i = 0; i != 10; ++i) vec2.insert( vec2.end(), vec2[0] ); return 0; } #包括 #包括 使用std::vector; 使用std::string; int main() { 向量vec1; vec1.推回(“你好”); 对于(int i=0;i!=10;++i) vec1.推回(vec1[0]); 向量vec2; vec2.推回(“你好”); 对于(int i=0;i!=10;++i) vec2.insert(vec2.end(),vec2[0]); 返回0; }

std::推回和插入之间的向量不一致崩溃(end(),x) 将此代码放入MS Visual C++ 2010中,编译(调试或发布),它会为插入()循环崩溃,但不是推送回退循环: #include <vector> #include <string> using std::vector; using std::string; int main() { vector<string> vec1; vec1.push_back("hello"); for (int i = 0; i != 10; ++i) vec1.push_back( vec1[0] ); vector<string> vec2; vec2.push_back("hello"); for (int i = 0; i != 10; ++i) vec2.insert( vec2.end(), vec2[0] ); return 0; } #包括 #包括 使用std::vector; 使用std::string; int main() { 向量vec1; vec1.推回(“你好”); 对于(int i=0;i!=10;++i) vec1.推回(vec1[0]); 向量vec2; vec2.推回(“你好”); 对于(int i=0;i!=10;++i) vec2.insert(vec2.end(),vec2[0]); 返回0; },c++,visual-studio-2010,vector,C++,Visual Studio 2010,Vector,问题是push_back()和insert()都通过引用获取新项, 当向量被重新分配更多空间时,新项在插入之前就失效了 GCC也应该有这个问题。我没有检查Clang,但这取决于它使用的是哪个STD库 MSVC2010在push_back()中有一些额外的代码,用于检测新项是否实际上是向量中的项。如果是这样,它会记录项的索引,并在分配内存后使用该索引插入项(而不是使用现在已失效的引用)——在内部使用_(_stdaddressof(_Val)) MSVC的额外代码是非标准的吗 我担心的是,我不确定在

问题是push_back()和insert()都通过引用获取新项, 当向量被重新分配更多空间时,新项在插入之前就失效了

GCC也应该有这个问题。我没有检查Clang,但这取决于它使用的是哪个STD库

MSVC2010在push_back()中有一些额外的代码,用于检测新项是否实际上是向量中的项。如果是这样,它会记录项的索引,并在分配内存后使用该索引插入项(而不是使用现在已失效的引用)——在内部使用_(_stdaddressof(_Val))

MSVC的额外代码是非标准的吗

我担心的是,我不确定在什么代码中我可能做过像vec.push_back(vec[1])这样的事情;或矢量插入(it,矢量[2]); 我必须浏览成百上千行使用push_-back和insert的代码,这只是我自己的代码。。。第三方库也可能受到影响

我假设使用这种技术可以使GCC以可怕的方式消亡(我看不到处理这种情况的额外代码,但valgrind在我的简单示例中没有检测到它,因此将更难测试)

如何最好地发现并避免犯此错误

MSVC2010的额外push_back()代码是否非标准?MSVC是否应该在找到以这种方式使用的向量时检测并断言?(即安全计算倡议)

我正在考虑黑客MSVC2010和GCC的头来检测这些情况

还有其他想法吗

谢谢, 保罗


PS:还请注意,如果您可以保证向量不需要调整大小,那么这种用法是非常好的(并且非常有效):最初我的印象是插入现有元素可能是未定义的行为;我不再相信是这样,原因如下:

根据,标准中没有禁止插入对现有元素的引用的语言。引用迭代器和引用无效的语言只能在操作完成后读取(在没有其他指示的情况下)作为引用行为

注意,根据规定,
插入(it,first,last)
的迭代器参数不应是序列中的迭代器;
push_back
中没有任何此类语言意味着特别允许引用序列(根据inclusio unius est exclusio alterius的法律原则)

看看你链接的bug报告,我猜MSVC在这个案例中的崩溃是由于他们的代码在C++11移动语义中被破坏而造成的,这不是故意的。在复制/移动现有元素之前,g++通过(我认为)将插入的元素复制到新分配内存中的适当位置来处理这种情况:

void insert(it, const T &t) {
    if (size() + 1 > capacity()) {
        T *new_data = (T *) malloc(sizeof(T) * capacity() * 2);
        new (&new_data[it - begin()]) T(t);
        // move [begin(), it) to [new_data, &new_data[it - begin()])
        // move [it, end()) to [&new_data[it - begin() + 1], &new_data[size() + 1])
    }
    ...
}
您可以使用自己的类模板包装
std::vector
,而不是对标题进行黑客攻击。如果要修改标准实现,请注意不要破坏确保不会发生重新分配的代码:

v.reserve(v.size() + 1);
v.push_back(v[0]);

在这里回答我自己的问题,

我发现一个与我的代码几乎相同的错误报告:

如上评论所述,其在MSVC 2012中显然是固定的

我深入研究了GCC代码,这里提到了可能相关的内容: 00326//三个操作的顺序由C++0x指定 00327//case,其中移动可能会改变属于的新元素 00328//到现有向量。这只是呼叫者的问题 00329//通过常量左值ref获取元素(见23.1/13)

但是有太多的假设让我弄不清楚它到底在干什么


因此,我想答案是升级到MSVC 2012,或者至少对头文件进行黑客攻击,这样我就知道还有什么地方需要小心了。

看看4.4的实现,当需要增加缓冲区调用时,
push_back
insert
首先复制新元素(这意味着别名不是一个问题,因为此时原始对象还没有被触及)然后是所有以前存在的元素。所以实现是好的


作为标准的一部分,没有对别名的限制,因此代码是兼容的,不应该存在未定义的行为。

好的,我在virtualbox上安装了Win8+MSVC2012来试用它。Geez Windows 8对鼠标很恼火,没有按钮可以按下,只是悬停,这在窗口中的屏幕上很难做到

结果很有趣,但仍然不一致

MSVC 2010:正如ecatmur所建议的,这个bug来自移动语义

问题是v.insert(v.end(),v[0])将选择insert(it,T&&val)方法,这在两个方面都是错误的: 1) 它可能导致v[0]的破坏。它似乎没有,这对我来说意味着const&reference被保留,新版本是通过复制而不是移动创建的。 2)在调整向量大小之前,代码路径不会复制val

请注意,由于push_back(&&)中的额外代码(黑客?),该问题没有很快被注意到-请参阅底部有关MSVC2012的更多注释

(请注意,insert(it,const&)将在调整向量大小之前首先正确复制新项,因此,如果选择了正确的方法,则会出现n
#include <vector>
#include <string>

using std::vector;
using std::string;

int main()
{
   vector<string> vec1;
   vec1.push_back("hello");

   for (int i = 0; i != 1000; ++i)
   {
       string temp = vec1[0];
      vec1.push_back( std::move(vec1[0]) );
   }

   vector<string> vec2;
   vec2.push_back("hello");

   for (int i = 0; i != 1000; ++i)
   {
       string temp = vec2[0];
      vec2.insert( vec2.end(), std::move(vec2[0]) );
   }

   return 0;
}
template<class _Valty>
iterator insert(const_iterator _Where, _Valty&& _Val)
iterator insert(const_iterator _Where, _Ty&& _Val)