push_back vs emplace_back

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

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

由于有一个push_back超载取右值引用,我不明白emplace_back的目的是什么?

除了什么访问者说:

由MSCV10提供的函数void emplace_back(Type&& _Val)是不符合和冗余的,因为正如你所说的那样,它严格地等同于push_back(Type&& _Val)

但真正的C ++ 0xforms的emplace_back真的很有用: void emplace_back(Args&&...) ;

而不是采取value_type它需要一个可变的参数列表,这意味着你现在可以完美地转发参数,并直接构造一个对象到一个没有临时的容器。

这是有用的,因为不pipe有多聪明的RVO和移动语义带来的表还有复杂的情况下,push_back可能会造成不必要的副本(或移动)。 例如,使用std::map的传统insert()函数,您必须创build一个临时的,然后将其复制到std::pair<Key, Value> ,然后将其复制到映射中:

 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 good m.insert(std::make_pair(4, Complicated(anInt, aDouble, aString))); // should be easier for the optimizer m.emplace(4, anInt, aDouble, aString); 

那为什么他们没有在MSVC中实现emplace_back的正确版本呢? 事实上,前段时间我也在窃听我,所以我在Visual C ++博客上提出了同样的问题。 这里是Microsoft的Visual C ++标准库实现的官方维护者Stephan T Lavavej的回答。

问:现在beta2 emplace函数只是某种占位符吗?

答:你可能知道,可变参数模板在VC10中没有实现。 我们用类似make_shared<T>() ,tuple和<functional>的新事物的预处理器来模拟它们。 这种预处理机器使用和维护相对困难。 另外,它会显着影响编译速度,因为我们必须重复包含子标题。 由于我们的时间限制和编译速度问题的结合,我们在emplace函数中没有模拟可变参数模板。

在编译器中实现可变参数模板时,可以预期我们将在库中利用它们,包括在我们的emplace函数中。 我们非常重视合规性,但不幸的是,我们不能一下子做所有事情。

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

emplace_back不应该采用vector::value_typetypes的参数,而应该将可变参数转发给附加项目的构造函数。

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

可以传递一个将被转发给拷贝构造函数的value_type

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

  std::vector<std::string> vec; vec.emplace_back(std::string("Hello")); // moves std::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 

emplace_back优化可以在下面的例子中演示。

对于emplace_back构造函数A (int x_arg)将被调用。 对于push_back首先调用A (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 &&) 

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

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

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