我对push_back
和emplace_back
之间的区别感到有点困惑。
void emplace_back(Type&& _Val);
void push_back(const Type& _Val);
void push_back(Type&& _Val);
由于push_back
重载采用右值引用,我不太明白emplace_back
的目的是什么?
答案 0 :(得分:487)
除了访客说的话:
MSCV10提供的函数void emplace_back(Type&& _Val)
是不符合和冗余的,因为正如您所指出的那样,它完全等同于push_back(Type&& _Val)
。
但emplace_back
的真正C ++ 0x形式非常有用:void emplace_back(Args&&...)
;
它取代value_type
而不是参数的可变参数列表,这意味着您现在可以完美地转发参数并直接将对象构造到容器中而不需要临时参数。
这很有用,因为无论RVO和移动语义多么聪明,仍然存在复杂的情况,其中push_back可能会进行不必要的复制(或移动)。例如,使用insert()
的传统std::map
函数,您必须创建一个临时函数,然后将其复制到std::pair<Key, Value>
中,然后将其复制到地图中:< / p>
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上问了同样的问题。以下是来自微软Visual C ++标准库实现的官方维护者Stephan T Lavavej的答案。
问:beta 2 emplace现在只是某种占位符吗?
答:正如您所知,可变参数模板 在VC10中没有实现。我们 用预处理器模拟它们 像这样的机器
make_shared<T>()
,元组和新的<functional>
中的内容。这个 预处理机器相对而言 难以使用和维护。也, 它会显着影响编译 速度,我们必须反复 包括子标题。由于一个 结合我们的时间限制 和编译速度问题,我们 没有模拟可变参数模板 在我们的emplace函数中。当可变参数模板是 在编译器中实现,你可以 期望我们会利用 他们在图书馆,包括在 我们的功能。我们采取 一致性非常严重,但是 不幸的是,我们无法做到一切 一下子。
这是一个可以理解的决定。每个尝试过一次用预处理器可怕的技巧模仿可变参数模板的人都知道这些东西有多恶心。
答案 1 :(得分:178)
emplace_back
不应该使用vector::value_type
类型的参数,而是转发到附加项的构造函数的可变参数。
template <class... Args> void emplace_back(Args&&... args);
可以传递将被转发到复制构造函数的value_type
。
因为它转发了参数,这意味着如果你没有rvalue,这仍然意味着容器将存储“复制”的副本,而不是移动的副本。
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
答案 2 :(得分:59)
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 &&)
答案 3 :(得分:8)
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
需要可复制或可移动类型来增长。
但是 note 这对std::map<Key, NonCopyableNonMovableObject>
有用,因为一旦你在地图中分配了一个条目,它就不再需要被移动或复制了,这与{ {1}},意味着您可以有效地使用vector
与既不可复制也不可移动的映射类型。
答案 4 :(得分:4)
列表中还有一个:
//构建元素。
emplace_back( “元素”);
//它将创建新对象,然后复制(或移动)其参数值。 的push_back(explicitDataType { “元件”});
答案 5 :(得分:4)
这里显示了一个很好的push_back和emplace_back代码。
http://en.cppreference.com/w/cpp/container/vector/emplace_back
您可以在push_back上看到移动操作,而不是在emplace_back上看到。
答案 6 :(得分:-1)
emplace_back
的特定用例:如果需要创建一个临时对象,然后将其放入容器中,请使用emplace_back
而不是push_back
。它将在容器内就地创建对象。
注意:
push_back
将创建一个临时对象并将其移动
放入容器。但是,很明显,就地构建会更多
比构造然后移动对象(通常涉及一些复制)更具性能。emplace_back
而不是push_back
来解决很多问题。 (请参见exceptions)