为什么不能重载push_back来完成emplace_back的工作?

46
首先,我知道这个问题(点此),但我认为我的问题不同。
我知道std :: vector <T> :: emplace_back是什么——我理解为什么要使用它而不是push_back()。它使用可变参数模板,允许我将多个参数转发到新元素的构造函数中。
但我不明白的是,为什么C ++标准委员会决定需要一个新成员函数。他们为什么不能只扩展push_back()的功能呢? 就我所看到的,push_back在C ++11中可以被重载为:
template <class... Args>
void push_back(Args&&... args);

这样做不会破坏向后兼容性,同时允许您传递N个参数,包括可能调用常规rvalue或复制构造函数的参数。事实上,GCC C++11的push_back()实现只是简单地调用了emplace_back()

  void push_back(value_type&& __x)
  { 
    emplace_back(std::move(__x)); 
  }

所以,我认为不需要使用 emplace_back()。他们只需要添加一个重载函数push_back(),该函数接受可变参数并将参数转发给元素构造函数即可。

我错了吗?是否有某些原因需要使用全新的函数?


1
这看起来很相似:https://dev59.com/E2855IYBdhLWcg3wg0nC - moka
7
好问题。@moka知道这两个函数的区别。他甚至链接到了那个特定的问题。 - user802003
它们有不同的含义,这些都在你提到的问题中得到了解释。例如,对于具有从int进行隐式转换的vector<SomeType> v;,比较v.emplace_back(123)v.push_back(123) - Gene Bushuyev
1
@Gene:好的,我已经比较过它们了,如果将push_back简单地修改为执行emplace_back的操作,我不明白它们之间有什么区别。你能否在这里详细解释一下? - Benjamin Lindley
这是库开发的标准做法。您不会更改接口,而是会创建一个新接口。然后允许用户以自己的节奏使用旧 API 实现新 API,从而使代码永远不会出错。 - AJG85
@Benjamin -- 很明显,在一个情况下你移动了临时变量,而在另一个情况下你直接构造了一个对象。这只是为了说明目的而提供的一个微不足道的例子,但它确实展示了区别。 - Gene Bushuyev
2个回答

41
如果T有显式转换构造函数,则emplace_backpush_back之间存在不同的行为。
struct X
{
    int val;
    X() :val() {}
    explicit X(int v) :val(v) {}
};

int main()
{
    std::vector<X> v;
    v.push_back(123);    // this fails
    v.emplace_back(123); // this is okay
}

你提出的更改意味着在这种情况下使用 push_back 是合法的,我想这不是期望的行为。我不知道这是否是原因,但这是我能想到的唯一解释。

7
在处理 unique_ptr 的容器时,使用 v.emplace_back(new foo) 比使用 v.push_back(std::unique_ptr<foo>(new foo)) 更加简单。但在这种情况下,应该能够显式请求进行转换。 - Alexandre C.
@AlexandreC.:实际上,emplace_back(new foo)不是异常安全的,所以这样做是错误的。 - user541686
@AlexandreC.:是的——具体来说,扩大向量所需的潜在分配。这里可以得出一个潜在的元教训:当实际上不需要使用它时,不要因为纯粹的打字懒惰而使用新功能。 - user541686
@AlexandreC.:我不确定你是在开玩笑,但如果你不坚持使用原始的方法(这对我来说也没问题),你应该只需使用push_back(make_unique<foo>())即可... - user541686
@Mehrdad 最近C++变得有点啰嗦了,不是吗?是的,模板确实是个玩笑。我完全同意你的看法,但当我写下那条评论时,我们正在使用没有make_unique支持的Visual Studio 2010,并且我们仍然偶尔编译代码。现在,我们在代码库中搜索newdelete - Alexandre C.
...并通过十几个模板来模拟使用make_unique,所有的代码都(令人讨厌地)位于namespace std下 ;) - Alexandre C.

0

这里有另一个例子。

说实话,这两个语义上是如此不同,它们的相似行为应该被视为一种偶然现象(因为C++具有特定语法的“复制构造函数”)。

除非你想要就地构造语义,否则你真的不应该使用emplace_back
你很少需要这样的东西。通常情况下,push_back才是你真正想要的。

#include <vector>

struct X { X(struct Y const &); };
struct Y { Y(int const &); operator X(); };

int main()
{
    std::vector<X> v;
    v.   push_back(Y(123));  // Calls Y::operator X() and Y::Y(int const &)
    v.emplace_back(Y(123));  // Calls X::X(Y const &) and Y::Y(int const &)
}

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