C++ 为什么我会使用 push_back 而不是 emplace_back?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10890653/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why would I ever use push_back instead of emplace_back?
提问by David Stone
C++11 vectors have the new function emplace_back
. Unlike push_back
, which relies on compiler optimizations to avoid copies, emplace_back
uses perfect forwarding to send the arguments directly to the constructor to create an object in-place. It seems to me that emplace_back
does everything push_back
can do, but some of the time it will do it better (but never worse).
C++11 向量具有新函数emplace_back
。与push_back
依赖编译器优化来避免复制的不同,它emplace_back
使用完美转发将参数直接发送到构造函数以就地创建对象。在我看来,emplace_back
它push_back
可以做所有可以做的事情,但有时它会做得更好(但永远不会更糟)。
What reason do I have to use push_back
?
我有什么理由使用push_back
?
采纳答案by David Stone
I have thought about this question quite a bit over the past four years. I have come to the conclusion that most explanations about push_back
vs. emplace_back
miss the full picture.
在过去的四年里,我对这个问题思考了很多。我得出的结论是,大多数关于push_back
vs. 的解释都emplace_back
没有全面了解。
Last year, I gave a presentation at C++Now on Type Deduction in C++14. I start talking about push_back
vs. emplace_back
at 13:49, but there is useful information that provides some supporting evidence prior to that.
去年,我在 C++Now 上发表了关于C++14 中的类型推导的演讲。我在 13:49开始谈论push_back
vs. emplace_back
,但在此之前有一些有用的信息提供了一些支持证据。
The real primary difference has to do with implicit vs. explicit constructors. Consider the case where we have a single argument that we want to pass to push_back
or emplace_back
.
真正的主要区别与隐式和显式构造函数有关。考虑我们想要传递给push_back
or的单个参数的情况emplace_back
。
std::vector<T> v;
v.push_back(x);
v.emplace_back(x);
After your optimizing compiler gets its hands on this, there is no difference between these two statements in terms of generated code. The traditional wisdom is that push_back
will construct a temporary object, which will then get moved into v
whereas emplace_back
will forward the argument along and construct it directly in place with no copies or moves. This may be true based on the code as written in standard libraries, but it makes the mistaken assumption that the optimizing compiler's job is to generate the code you wrote. The optimizing compiler's job is actually to generate the code you would have written if you were an expert on platform-specific optimizations and did not care about maintainability, just performance.
在您的优化编译器掌握了这一点之后,就生成的代码而言,这两个语句之间没有区别。传统的观点是,push_back
将构造一个临时对象,然后将其移入,v
而emplace_back
将继续推进论证并直接将其构造到位,没有复制或移动。根据标准库中编写的代码,这可能是正确的,但它错误地假设优化编译器的工作是生成您编写的代码。优化编译器的工作实际上是生成代码,如果您是特定于平台的优化方面的专家,并且不关心可维护性,只关心性能,那么您会编写代码。
The actual difference between these two statements is that the more powerful emplace_back
will call any type of constructor out there, whereas the more cautious push_back
will call only constructors that are implicit. Implicit constructors are supposed to be safe. If you can implicitly construct a U
from a T
, you are saying that U
can hold all of the information in T
with no loss. It is safe in pretty much any situation to pass a T
and no one will mind if you make it a U
instead. A good example of an implicit constructor is the conversion from std::uint32_t
to std::uint64_t
. A bad example of an implicit conversion is double
to std::uint8_t
.
这两个语句之间的实际区别在于,更强大的emplace_back
将调用任何类型的构造函数,而更谨慎的push_back
将仅调用隐式构造函数。隐式构造函数应该是安全的。如果您可以U
从 a隐式构造 a T
,那么您是说U
可以T
毫无损失地保存所有信息。在几乎任何情况下通过 a 都是安全的,T
如果你U
改为a ,没有人会介意。隐式构造函数的一个很好的例子是从std::uint32_t
to的转换std::uint64_t
。隐式转换的一个不好的例子是double
to std::uint8_t
。
We want to be cautious in our programming. We do not want to use powerful features because the more powerful the feature, the easier it is to accidentally do something incorrect or unexpected. If you intend to call explicit constructors, then you need the power of emplace_back
. If you want to call only implicit constructors, stick with the safety of push_back
.
我们希望在我们的编程中保持谨慎。我们不想使用强大的功能,因为功能越强大,就越容易意外地做一些不正确或意外的事情。如果您打算调用显式构造函数,那么您需要emplace_back
. 如果您只想调用隐式构造函数,请坚持使用push_back
.
An example
一个例子
std::vector<std::unique_ptr<T>> v;
T a;
v.emplace_back(std::addressof(a)); // compiles
v.push_back(std::addressof(a)); // fails to compile
std::unique_ptr<T>
has an explicit constructor from T *
. Because emplace_back
can call explicit constructors, passing a non-owning pointer compiles just fine. However, when v
goes out of scope, the destructor will attempt to call delete
on that pointer, which was not allocated by new
because it is just a stack object. This leads to undefined behavior.
std::unique_ptr<T>
有一个来自 的显式构造函数T *
。因为emplace_back
可以调用显式构造函数,传递一个非拥有指针编译就好了。但是,当v
超出范围时,析构函数将尝试调用delete
那个没有分配的指针,new
因为它只是一个堆栈对象。这会导致未定义的行为。
This is not just invented code. This was a real production bug I encountered. The code was std::vector<T *>
, but it owned the contents. As part of the migration to C++11, I correctly changed T *
to std::unique_ptr<T>
to indicate that the vector owned its memory. However, I was basing these changes off my understanding in 2012, during which I thought "emplace_back does everything push_back can do and more, so why would I ever use push_back?", so I also changed the push_back
to emplace_back
.
这不仅仅是发明的代码。这是我遇到的一个真正的生产错误。代码是std::vector<T *>
,但它拥有内容。作为迁移到C ++ 11的一部分,我正确地改变T *
,以std::unique_ptr<T>
指示该矢量拥有它的存储器。但是,我在 2012 年基于我的理解进行了这些更改,在此期间我认为“emplace_back 可以完成 push_back 可以做的所有事情等等,那么我为什么要使用 push_back?”,所以我也push_back
将emplace_back
.
Had I instead left the code as using the safer push_back
, I would have instantly caught this long-standing bug and it would have been viewed as a success of upgrading to C++11. Instead, I masked the bug and didn't find it until months later.
如果我将代码保留为使用更安全的push_back
,我会立即发现这个长期存在的错误,并且它会被视为升级到 C++11 的成功。相反,我掩盖了错误,直到几个月后才发现它。
回答by Luc Danton
push_back
always allows the use of uniform initialization, which I'm very fond of. For instance:
push_back
总是允许使用我非常喜欢的统一初始化。例如:
struct aggregate {
int foo;
int bar;
};
std::vector<aggregate> v;
v.push_back({ 42, 121 });
On the other hand, v.emplace_back({ 42, 121 });
will not work.
另一方面,v.emplace_back({ 42, 121 });
将不起作用。
回答by user541686
Backwards compatibility with pre-C++11 compilers.
向后兼容 C++11 之前的编译器。
回答by Marc
Some library implementations of emplace_back do not behave as specified in the C++ standard including the version that ship with Visual Studio 2012, 2013 and 2015.
emplace_back 的某些库实现的行为与 C++ 标准中指定的不同,包括 Visual Studio 2012、2013 和 2015 附带的版本。
In order to accommodate known compiler bugs, prefer usingstd::vector::push_back()
if the parameters reference iterators or other objects which will be invalid after the call.
为了适应已知的编译器错误,std::vector::push_back()
如果参数引用迭代器或其他在调用后无效的对象,则首选使用。
std::vector<int> v;
v.emplace_back(123);
v.emplace_back(v[0]); // Produces incorrect results in some compilers
On one compiler, v contains the values 123 and 21 instead of the expected 123 and 123. This is due to the fact that the 2nd call to emplace_back
results in a resize at which point v[0]
becomes invalid.
在一个编译器上,v 包含值 123 和 21,而不是预期的 123 和 123。这是因为第二次调用emplace_back
导致调整大小,此时v[0]
无效。
A working implementation of the above code would use push_back()
instead of emplace_back()
as follows:
上述代码的工作实现将使用push_back()
而不是emplace_back()
如下:
std::vector<int> v;
v.emplace_back(123);
v.push_back(v[0]);
Note: The use of a vector of ints is for demonstration purposes. I discovered this issue with a much more complex class which included dynamically allocated member variables and the call to emplace_back()
resulted in a hard crash.
注意:整数向量的使用是为了演示目的。我在一个更复杂的类中发现了这个问题,其中包括动态分配的成员变量和emplace_back()
导致硬崩溃的调用。