关于push_back和emplace_back之间的区别,我有点困惑。
void emplace_back(Type&& _Val);
void push_back(const Type& _Val);
void push_back(Type&& _Val);
由于有一个push_back重载取右值引用,我不太清楚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的目的是什么?
当前回答
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
其他回答
还有一个关于列表的例子:
// 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的优化可以在下一个示例中演示。
对于emplace_back,将调用构造函数A (int x_arg)。和 首先调用push_back A (int x_arg),然后调用move A (A &&rhs)。
当然,构造函数必须被标记为显式的,但对于当前的示例来说,删除显式是很好的。
#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 &&)
除了访客所说的:
MSCV10提供的函数void emplace_back(Type&& _Val)是不一致的和冗余的,因为正如你所注意到的,它严格等同于push_back(Type&& _Val)。
但是真正的c++ 0x形式的emplace_back非常有用:void emplace_back(Args&&…);
它不采用value_type,而是采用可变参数列表,这意味着现在可以完美地转发参数并直接将对象构造到容器中,而根本不需要临时变量。
这很有用,因为无论RVO和move语义有多聪明,仍然有一些复杂的情况,在这些情况下,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的回答。
Q: Are beta 2 emplace functions just some kind of placeholder right now? A: As you may know, variadic templates aren't implemented in VC10. We simulate them with preprocessor machinery for things like make_shared<T>(), tuple, and the new things in <functional>. This preprocessor machinery is relatively difficult to use and maintain. Also, it significantly affects compilation speed, as we have to repeatedly include subheaders. Due to a combination of our time constraints and compilation speed concerns, we haven't simulated variadic templates in our emplace functions. When variadic templates are implemented in the compiler, you can expect that we'll take advantage of them in the libraries, including in our emplace functions. We take conformance very seriously, but unfortunately, we can't do everything all at once.
这是可以理解的决定。每个尝试过用预处理器模仿可变模板的人都知道这个东西有多恶心。
emplace_back的特定用例:如果您需要创建一个临时对象,然后将其推入容器,请使用emplace_back而不是push_back。它将在容器内就地创建对象。
注:
上面例子中的Push_back将创建一个临时对象并移动它 放进容器里。然而,用于emplace_back的in-place结构将更多 性能比构造然后移动对象(通常涉及一些复制)。 一般来说,在所有情况下都可以使用emplace_back而不是push_back,没有太多问题。(见异常)
遵从emplace_back的实现在添加到vector时将参数转发给vector<Object>::value_typeconstructor。我记得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用于既不可复制也不可移动的映射类型。