C++ 11向量具有新功能emplace_back.与push_back依赖于编译器优化以避免副本的情况不同,emplace_back使用完美转发将参数直接发送到构造函数以就地创建对象.在我看来,emplace_back一切都push_back可以做到,但有些时候它会做得更好(但从来没有更糟).
我有什么理由使用push_back?
Dav*_*one 143
在过去的四年里,我对这个问题进行了相当多的考虑.我得出的结论是,关于push_backvs.的大部分解释都emplace_back错过了全貌.
去年,我在C++中演示了C++中的类型演绎14.我在13:49 开始谈论push_backvs. emplace_back,但有一些有用的信息在此之前提供了一些支持证据.
真正的主要区别与隐式和显式构造函数有关.考虑我们想要传递给push_back或的单个参数的情况emplace_back.
std::vector<T> v;
v.push_back(x);
v.emplace_back(x);
Run Code Online (Sandbox Code Playgroud)
在优化编译器得到它之后,就生成的代码而言,这两个语句之间没有区别.传统的观点认为,push_back将构造一个临时对象,然后将得到搬进v而emplace_back将转发沿参数,没有复制或移动到位直接构造它.基于标准库中编写的代码,这可能是正确的,但它会错误地假设优化编译器的工作是生成您编写的代码.优化编译器的工作实际上是生成您编写的代码,如果您是特定于平台的优化专家,并且不关心可维护性,只关心性能.
这两个语句之间的实际区别在于,更强大的emplace_back将调用任何类型的构造函数,而更谨慎的push_back只调用隐式的构造函数.隐含的构造函数应该是安全的.如果你可以隐含地构造一个U来自a T,你就是说U可以保存所有信息T而不会丢失.在几乎任何情况下都可以安全地通过,T而且没有人会介意的话U.隐式构造函数的一个很好的例子是转换std::uint32_t为std::uint64_t.的隐式转换的一个坏的例子是double对std::uint8_t.
我们希望在编程时保持谨慎.我们不想使用强大的功能,因为功能越强大,就越容易意外地做出错误或意外的事情.如果你打算调用显式构造函数,那么你需要它的强大功能emplace_back.如果你只想调用隐式构造函数,请坚持使用push_back.
一个例子
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
Run Code Online (Sandbox Code Playgroud)
std::unique_ptr<T>有一个明确的构造函数T *.因为emplace_back可以调用显式构造函数,所以传递一个非拥有指针就可以了.但是,当v超出范围时,析构函数将尝试调用delete该指针,该指针未被分配,new因为它只是一个堆栈对象.这导致未定义的行为.
这不仅仅是发明的代码.这是我遇到的真正的生产错误.代码是std::vector<T *>,但它拥有内容.作为迁移到C++ 11的一部分,我正确地改变T *,以std::unique_ptr<T>指示该矢量拥有它的存储器.不过,我在2012年基础这些改变了我的理解,在这期间我还以为"emplace_back做一切的push_back可以做多,所以我为什么会用不完的push_back?",所以我也改变了push_back来emplace_back.
如果我把代码保留为使用更安全push_back,我会立即抓住这个长期存在的错误,它将被视为升级到C++ 11的成功.相反,我掩盖了这个错误,直到几个月后才找到它.
Luc*_*ton 114
push_back总是允许使用统一初始化,这是我非常喜欢的.例如:
struct aggregate {
int foo;
int bar;
};
std::vector<aggregate> v;
v.push_back({ 42, 121 });
Run Code Online (Sandbox Code Playgroud)
另一方面,v.emplace_back({ 42, 121 });不会起作用.
Meh*_*dad 79
向后兼容前C++ 11编译器.
Mar*_*arc 67
emplace_back的某些库实现的行为不符合C++标准中的规定,包括Visual Studio 2012,2013和2015附带的版本.
为了适应已知的编译器错误,std::vector::push_back()如果参数引用迭代器或在调用后无效的其他对象,则更喜欢使用.
std::vector<int> v;
v.emplace_back(123);
v.emplace_back(v[0]); // Produces incorrect results in some compilers
Run Code Online (Sandbox Code Playgroud)
在一个编译器上,v包含值123和21而不是预期的123和123.这是因为第二次调用emplace_back导致调整大小,此时该值v[0]变为无效.
上述代码的工作实现将使用push_back()而不是emplace_back()如下:
std::vector<int> v;
v.emplace_back(123);
v.push_back(v[0]);
Run Code Online (Sandbox Code Playgroud)
注意:使用int的向量是为了演示目的.我发现这个问题有一个更复杂的类,其中包括动态分配的成员变量和调用emplace_back()导致硬崩溃.
| 归档时间: |
|
| 查看次数: |
47210 次 |
| 最近记录: |