我对 push_back
和 emplace_back
之间的区别有点困惑。
void emplace_back(Type&& _Val);
void push_back(const Type& _Val);
void push_back(Type&& _Val);
由于存在采用右值引用的 push_back
重载,我不太明白 emplace_back
的目的是什么?
template <class _Valty> void emplace_back(_Valty&& _Val)
版本,它提供了对 explicit
单参数构造函数的完美转发。
push_back
优于 emplace_back
的情况?我能想到的唯一情况是,如果一个类在某种程度上是可复制的(T&operator=(constT&)
)但不可构造(T(constT&)
),但我想不出为什么有人会想要这样。
除了访客所说的:
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 上问了同样的问题。这是来自微软 Visual C++ 标准库实现的官方维护者 Stephan T Lavavej 的回答。
问:beta 2 emplace 函数现在只是某种占位符吗?答:您可能知道,可变参数模板在 VC10 中没有实现。我们使用预处理器机制模拟它们,例如 make_shared
这是一个可以理解的决定。每个尝试过一次用预处理器可怕的技巧模拟可变参数模板的人都知道这些东西有多恶心。
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
s
,这不是很危险吗?
vec.emplace_back("Hello")
将起作用,因为 const char*
参数将被转发到 string
构造函数。这就是emplace_back
的重点。
std::vector
。空 std::vector
是有效状态,但您不能对其调用 front()
。这意味着任何没有前置条件的函数仍然可以被调用(并且析构函数永远不能有前置条件)。
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 &&)
v.emplace_back(x);
的代码后来到这里,其中 x 是显式可移动构造但只能显式复制构造。 emplace_back
是“隐式”显式的这一事实使我认为我的附加功能应该是 push_back
。想法?
a.emplace_back
,将调用移动构造函数!
"A (A &&)\n"
来检查是否打印 "A (A &&) on " << rhs.x << "\n"
。您可以看到它in this edited code snippet。
列表的另一个示例:
// constructs the elements in place.
emplace_back("element");
// creates a new object and then copies (or moves) that object.
push_back(ExplicitDataType{"element"});
emplace_back
它是将参数转发给构造函数的容器 - 也就是在调用之前或之后你手头没有任何对象。 “改变对象本身”是什么意思?容器中只有对象。或者您事先创建对象 - 在这种情况下,它的作用与 push_back 相同。
emplace_back
的特定用例:如果您需要创建一个临时对象,然后将其推送到容器中,请使用 emplace_back
而不是 push_back
。它将在容器内就地创建对象。
笔记:
上述情况下的 push_back 将创建一个临时对象并将其移动到容器中。但是,用于 emplace_back 的就地构造比构造然后移动对象(这通常涉及一些复制)具有更高的性能。一般来说,在所有情况下,您都可以使用 emplace_back 而不是 push_back,而不会出现太大问题。 (见例外)
此处显示了 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
与既不可复制也不可移动的映射类型一起使用。
pair<const int,Complicated>
没有一个构造函数,它接受一个 int、另一个 int、一个 double 和一个字符串作为第四个参数。但是,您可以使用它的分段构造函数直接构造这个对对象。当然,语法会有所不同:m.emplace(std::piecewise,std::forward_as_tuple(4),std::forward_as_tuple(anInt,aDouble,aString));
emplace_back
,只要它在添加可变参数模板时在 Visual C++ 中实现:msdn.microsoft.com/en-us/library/hh567368.aspx