push_backvsemplace_back

我对push_backemplace_back之间的区别有点困惑。

void emplace_back(Type&& _Val);void push_back(const Type& _Val);void push_back(Type&& _Val);

因为有一个push_back重载取一个右值引用,我不太明白emplace_back的目的是什么?

566861 次浏览

emplace_back不应采用vector::value_type类型的参数,而是转发给附加项的构造函数的可变参数。

template <class... Args> void emplace_back(Args&&... args);

可以传递一个value_type,它将被转发到复制构造函数。

因为它转发参数,这意味着如果你没有右值,这仍然意味着容器将存储一个“复制”的副本,而不是移动的副本。

 std::vector<std::string> vec;vec.emplace_back(std::string("Hello")); // movesstd::string s;vec.emplace_back(s); //copies

但是上面的内容应该与push_back所做的相同。它可能更适用于以下用例:

 std::vector<std::pair<std::string, std::string> > vec;vec.emplace_back(std::string("Hello"), std::string("world"));// should end up invoking this constructor://template<class U, class V> pair(U&& x, V&& y);//without making any copies of the strings

除了游客所说的:

MSCV10提供的函数void emplace_back(Type&& _Val)是不一致的和冗余的,因为正如您所注意到的,它严格等价于push_back(Type&& _Val)

但是emplace_back的C++0x形式非常有用:#1

而不是采用value_type,它采用可变参数列表,因此这意味着您现在可以完美地转发参数并将对象直接构造到容器中,而无需临时。

这很有用,因为无论RVO和移动语义给表带来多少聪明,仍然存在push_back可能进行不必要复制(或移动)的复杂情况。例如,使用std::map的传统insert()函数,你必须创建一个临时函数,然后将其复制到std::pair<Key, Value>中,然后将其复制到map中:

std::map<int, Complicated> m;int anInt = 4;double aDouble = 5.0;std::string aString = "C++";
// cross your finger so that the optimizer is really goodm.insert(std::make_pair(4, Complicated(anInt, aDouble, aString)));
// should be easier for the optimizerm.emplace(4, anInt, aDouble, aString);

那么,为什么他们没有在MSVC中实现正确版本的emplace_back呢?实际上,它不久前也困扰着我,所以我在视觉C++博客上问了同样的问题。这是微软VisualC++标准库实现的官方维护者Stephan T Lavavej的回答。

问:Beta 2的嵌入功能现在只是某种占位符吗?

A:如你所知,可变参数模板在VC10中没有实现。我们用预处理器模拟机器用于诸如make_shared<T>()、元组和新<functional>中的东西。这个预处理器相对难以使用和维护。而且,它显着影响编译速度,因为我们必须反复包括副标题。由于结合我们的时间限制和编译速度问题,我们还没有模拟可变参数模板在我们的emplace函数中。

当可变参数模板在编译器中实现,您可以希望我们能利用它们在图书馆,包括在我们的安置功能。我们采取非常认真,但是不幸的是我们不能什么都做一瞬间

这是一个可以理解的决定。每个尝试过一次用预处理器可怕的技巧模拟可变参数模板的人都知道这东西有多恶心。

emplace_back符合的实现将在添加到向量时将参数转发到vector<Object>::value_type构造函数。我记得Visual Studio不支持可变参数模板,但Visual Studio 2013 RC将支持可变参数模板,所以我想会添加一个符合要求的签名。

对于emplace_back,如果您将参数直接转发到vector<Object>::value_type构造函数,严格来说,对于emplace_back函数,您不需要一个可移动或可复制的类型。在vector<NonCopyableNonMovableObject>的情况下,这是没有用的,因为vector<Object>::value_type需要一个可复制或可移动的类型来生长。

但是注意,这可能对std::map<Key, NonCopyableNonMovableObject>有用,因为一旦你在map中分配了一个条目,它就不需要再被移动或复制了,不像vector,这意味着你可以有效地使用std::map和一个既不可复制也不可移动的映射类型。

可以在下一个示例中演示emplace_back的优化。

Foremplace_back构造函数A (int x_arg)将被调用。对于push_backA (int x_arg)先调用,move A (A &&rhs)后调用。

当然,构造函数必须标记为explicit,但对于当前示例,最好删除显式性。

#include <iostream>#include <vector>class A{public:A (int x_arg) : x (x_arg) { std::cout << "A (x_arg)\n"; }A () { x = 0; std::cout << "A ()\n"; }A (const A &rhs) noexcept { x = rhs.x; std::cout << "A (A &)\n"; }A (A &&rhs) noexcept { x = rhs.x; std::cout << "A (A &&)\n"; }
private:int x;};
int main ()\{\{std::vector<A> a;std::cout << "call emplace_back:\n";a.emplace_back (0);}{std::vector<A> a;std::cout << "call push_back:\n";a.push_back (1);}return 0;}

输出:

call emplace_back:A (x_arg)
call push_back:A (x_arg)A (A &&)

再举一个列表的例子:

// constructs the elements in place.emplace_back("element");
// creates a new object and then copies (or moves) that object.push_back(ExplicitDataType{"element"});

此处显示了push_back和emplace_back的漂亮代码。

http://en.cppreference.com/w/cpp/container/vector/emplace_back

您可以在push_back而不是emplace_back上看到移动操作。

emplace_back的具体用例:如果您需要创建一个临时对象,然后将其推送到容器中,请使用emplace_back而不是push_back。它将在容器内就地创建对象。

备注:

  1. push_back在上述情况下将创建一个临时对象并移动它进入容器。然而,用于emplace_back的就地构造会更多性能比构建然后移动对象(通常涉及一些复制)。
  2. 一般来说,您可以在所有情况下使用emplace_back而不是push_back,没有太大问题。(见例外