C++ MSVC中std::vector::push_的实现
我探索了msvc 2013 STL实现,发现了std::vector::push_back的实现:C++ MSVC中std::vector::push_的实现,c++,visual-studio,stl,C++,Visual Studio,Stl,我探索了msvc 2013 STL实现,发现了std::vector::push_back的实现: void push_back(const value_type& _Val) { // insert element at end if (_Inside(_STD addressof(_Val))) // <-- is this check really necessary? { // push back an element
void push_back(const value_type& _Val)
{ // insert element at end
if (_Inside(_STD addressof(_Val))) // <-- is this check really necessary?
{ // push back an element
size_type _Idx = _STD addressof(_Val) - this->_Myfirst;
if (this->_Mylast == this->_Myend)
_Reserve(1);
this->_Getal().construct(this->_Mylast,
this->_Myfirst[_Idx]);
++this->_Mylast;
}
else
{ // push back a non-element
if (this->_Mylast == this->_Myend)
_Reserve(1);
this->_Getal().construct(this->_Mylast,
_Val);
++this->_Mylast;
}
}
void push\u back(常量值类型和值)
{//在末尾插入元素
如果(_in(_stdaddressof(_Val))/_Myfirst;
如果(this->\u Mylast==this->\u Myend)
_储备(1);
this->_Getal().construct(this->_Mylast,
这个->\u Myfirst[\u Idx]);
++这是我的最后一次;
}
其他的
{//推回非元素
如果(this->\u Mylast==this->\u Myend)
_储备(1);
this->_Getal().construct(this->_Mylast,
_Val);
++这是我的最后一次;
}
}
我有个问题:这是支票吗
如果(_in(_stdaddressof(_Val)))
真的有必要吗?此条件检查值是否属于此向量。例如,在以下情况下,此条件为真:
std::vector<int> v(1);
v.push_back(v[0]);
标准:向量v(1);
v、 推回(v[0]);
同一向量的元素的push_back与其他值之间有什么区别?我不知道该额外检查是否由标准强制执行,但它避免了一个细微的错误,如果您将一个元素本身推入向量中,可能会发生这种错误 假设你这样做,就像你的例子一样
std::vector<int> v(1);
v.push_back(v[0]);
标准:向量v(1);
v、 推回(v[0]);
在不执行该检查的实现中。现在,如果向量的容量大于1,那么一切都很好,v[0]
只是在正确的位置进行复制构造
但是如果向量必须重新分配,会发生什么呢?在这种情况下,传递给push_back
的v[0]
引用在重新分配后立即失效,因此push_back
将尝试在向量内复制一个不再存在的对象
您发布的实现中的代码通过检查引用是否指向向量中的元素来避免问题,在这种情况下,它会记录其索引。重新分配后,即使引用无效,索引仍然正确,因此可以在没有风险的情况下执行复制。两个代码路径中的代码明显不同:
construct(this->_Mylast, this->_Myfirst[_Idx])
与之相比:
construct(this->_Mylast, _Val)
原因当然是,如果向量的容量耗尽,它需要重新分配其存储,这会使引用无效
如果参数
\u Val
,不是向量的一部分,那么它就没有相关性,但是如果是,那么在重新分配之后我们就不能再使用它了。因此,在第一个代码路径中,\u Val
是向量的一部分,该值由其向量索引引用,而不是由原始函数参数引用。您还没有回答自己的问题吗?不,正如我看到的,在这两种情况下,我们都有相同的代码,这个检查有意义吗?@user3290628:After\u Reserve(1)
,\u val
不再是有效的引用。它不是完全相同的代码。在一种情况下,使用这个向量的元素,旧值通过可能通过缓冲区重新分配进行的复制来访问。在另一种情况下,只需要一些值,就可以直接访问。MS实现中的其他向量修改操作是否有等价物?@KerrekSB:可能实现是这样的。我可以想象一个不同的实现,它不需要将此作为特例。@KerrekSB:澄清。IIRC的其他实现没有这样的检查,如果您将向量的一个元素压入其自身,则会失败。我不记得这是一个实现错误还是标准没有要求它。@MatteoItalia:Hm,听起来很奇怪:“它不是强制的,但它避免了一个微妙的错误”。。。难道不知道一致性实现没有bug吗?(标准中没有规定不要求v.push_back(v.back())
工作。)AFAICS标准中没有添加新项目参考不得位于向量内的前提条件的语言。因此,需要向量实现来正确处理该问题。我认为这是移动语义的一个很好的例子,如果std::vector
是现在设计的,那么它就不会有引用参数了。@Cheersandhth.-Alf:我觉得最后一点有问题。仅仅因为现在可以有效地设计可移动的类型,并不意味着标准库容器应该完全需要这样的类型才能达到模糊的效率。通过引用接受可推式值似乎是非常明智的。这是一个很好的例子,说明在一个高质量的库中,您不应该为了方便库编写者而进行优化,而是为了为消费者提供价值。