为什么vector::push_back有两个重载?

32
为什么vector::push_back没有采用转发引用(forwarding reference)而是有两种重载?我读过一些资料,只有当函数对lvalue和rvalue有不同的处理时才需要重载,那么vector::push_back的这两种重载除了移动/复制之外还有哪些区别呢?

我认为push_back的转发引用版本只是被称为emplace_back - Barry
1
@Barry emplace_back 接受转发引用作为元素类型的构造函数的参数。 - template boy
@templateboy 我不理解强调点在哪里。push_back(T&&)也将其参数移动到元素类型的构造函数中。 - Barry
1
@Barry 不对。T 是向量的元素类型,但 emplace_back 接受 Args&&... args 并将 args... 转发到构造函数,就好像通过 element_type(std :: forward <Args> (args)...) 一样。而 push_back 不会这样做。 - template boy
1
但是,如果value_type具有复制构造函数,则可以使用emplace_back作为push_back的更一般版本。 - juanchopanza
1个回答

53

我做这个工作主要是因为情况的发展。在 C++11 之前,只有:

vector<T>::push_back(const T&);

随着rvalue引用的引入,我建议增加以下重载:

vector<T>::push_back(T&&);

改变原始签名为以下内容:

template <class U> vector<T>::push_back(U&&);

这个决定的一部分是由于对向后兼容性的担忧(无论是否有必要),以及为了缓解厂商和委员会其他成员的担忧,即这只是一个功能的简单添加,而不是对现有功能的更改。

如果我今天从头重新设计vector,我会认真考虑只使用:

template <class U> vector<T>::push_back(U&&);

或许只是这样:
template <class ...Args> vector<T>::emplace_back(Args&& ...);

您可能想要了解更多详细信息,请参见N1858

为什么不按值push_back

此问题已被标记为重复:

为什么C++11 std容器具有按引用传递和按rvalue插入/推送方法?

该问题询问此问题。 所以我认为在这个答案中实际上需要解决这个方面是一件礼貌的事情...

对于左值和x值,与按引用解决方案相比,push_back(T)将花费额外的移动构造。 x值需要2次移动构造,而左值需要1次复制构造和1次移动构造。

相反,使用当前设计,左值成本为1次复制构造,而x值成本为1次移动构造。

对于某些类型T,移动构造并不便宜。 对于vector<T>假设T总是可以廉价移动的设计选择是不合适的。 例如,如果Tstd::array<double,100>怎么办?更改为按值设计将需要2个复制构造而不是1个来push_back(除prvalue外)。

按值的解决方案确实具有优点,并且应该在某些时候使用。只是vector<T> :: push_back()不是这些时候之一。


那两个函数中是否存在代码重复? - template boy
1
@templateboy:在libc++实现中,内联的“快速路径”(不需要重新分配)中存在非常少量的代码重复,但“慢速路径”是一个非内联的私有函数,两个重载都会转发到它。 - Howard Hinnant
6
v.push_back({aggregate_initialization}); 在你的“如果今天设计”的情况下会出现问题。完美转发并不完美。 - Yakk - Adam Nevraumont
@HowardHinnant您说,如果您今天从头开始设计它,您会考虑只有U&&版本。这是否需要对lvalue进行两次移动?对于移动构造不便宜的类型来说,这可能会更糟吧? - amfcosta
2
@amfcosta:第一点:我说的是“考虑”。我没有投入时间来测试和实现这些考虑。第二点:不,lvalues将绑定到转发引用U&&而不需要移动或复制。然后在内部,代码将forward<U>(u)构造一个T。假设UT,那就是1个复制构造。我可能还会限制U,使得is_constructible<T, U>{} - Howard Hinnant

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接