2010-11-29 69 views
469

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

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

是有push_back超载服用右值引用我不太明白的emplace_back目的为?

+11

一些很好的阅读在这里:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2642.pdf – 2010-11-29 14:00:37

+14

请注意,(如托马斯下文所述),问题中的代码来自MSVS的* C++ 0x模拟*,而不是C++ 0x的实际内容。 – me22 2010-12-21 05:50:14

+4

需要阅读的更好的论文是:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2345.pdf。 N2642主要是标准的措词; N2345是解释和激励这个想法的论文。 – Alan 2013-03-13 23:10:48

回答

384

除了什么游客说:

由MSCV10提供的功能void emplace_back(Type&& _Val)是不符合标准和多余的,因为当你注意到它是完全等同于push_back(Type&& _Val)

但是真正的C++ 0x表格emplace_back真的很有用:void emplace_back(Args&&...);

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

这很有用,因为无论有多聪明RVO和移动语义带来的表格仍然存在复杂情况,push_back可能会产生不必要的副本(或移动)。例如,具有std::map传统insert()功能,你必须创建一个临时的,那么这将被复制到一个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++ blog上问了同样的问题。这里是Microsoft的Visual C++标准库实现的官方维护者Stephan T Lavavej的回答。

问:现在beta2 emplace函数只是某种占位符吗?你可能知道,可变参数模板 没有在VC10中实现。我们 模拟它们与预处理器 物联网 make_shared<T>(),元组和 东西<functional>。这 预处理机器相对 难以使用和维护。另外, 它会显着影响编译 的速度,因为我们不得不重复包含子标题 。由于我们的时间限制 和编译速度问题的 的组合,我们 尚未在我们的emplace函数中模拟可变参数模板 。

当可变参数模板是 在编译器实现的,你可以 预计我们将在图书馆采取的 他们的优势,包括在 我们布设功能。我们非常重视 的一致性,但不幸的是,我们不能一次完成所有的事情 。

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

146

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")); // 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 
7

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>是有用的,因为一旦你分配在地图中的条目,它并不需要移动或复制过了,不像vector,这意味着你可以有效地使用std::map使用既不可复制也不可移动的映射类型。

25

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

对于emplace_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 &&) 
2

还有一个在列表中的情况下:

//构造代替的元件。
emplace_back(“element”);

//它将创建新的对象,然后复制(或移动)其参数值。 push_back(explicitDataType {“element”});