[英]push_back vs emplace_back
我对push_back
和emplace_back
之间的区别有点困惑。
void emplace_back(Type&& _Val);
void push_back(const Type& _Val);
void push_back(Type&& _Val);
由于存在采用右值引用的push_back
重载,我不太明白emplace_back
的目的是什么?
除了访客所说的:
void emplace_back(Type&& _Val)
提供的函数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>
,然后将其复制到 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 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++ 博客上问了同样的问题。 这是微软 Visual C++ 标准库实现的官方维护者 Stephan T Lavavej 的回答。
问:beta 2 emplace 函数现在只是某种占位符吗?
答:您可能知道,VC10 中没有实现可变参数模板。 我们使用预处理器机制模拟它们,例如
make_shared<T>()
、元组和<functional>
的新事物。 这种预处理机器相对难以使用和维护。 此外,它会显着影响编译速度,因为我们必须反复包含子标题。 由于我们的时间限制和编译速度问题,我们没有在 emplace 函数中模拟可变参数模板。当在编译器中实现可变参数模板时,您可以预期我们将在库中利用它们,包括在我们的 emplace 函数中。 我们非常重视一致性,但不幸的是,我们无法一次完成所有事情。
这是一个可以理解的决定。 每个尝试过使用预处理器可怕技巧模拟可变参数模板的人都知道这东西有多恶心。
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
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 &&)
列表的另一个示例:
// 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
实现会将参数转发给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
有效地使用既不可复制也不可移动的映射类型。
emplace_back
特定用例:如果您需要创建一个临时对象,然后将其推送到容器中,请使用emplace_back
而不是push_back
。 它将在容器内就地创建对象。
笔记:
push_back
将创建一个临时对象并将其移动到容器中。 但是,用于emplace_back
的就地构造比构造然后移动对象(通常涉及一些复制)的性能更高。emplace_back
而不是push_back
没有太大问题。 (见例外)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.