我可以通过用std::vector::push_back
替换emplace_back
并用C ++ 11编译器编译来破坏有效的C ++ 03程序吗?从阅读emplace_back
参考我收集它不应该发生,但我承认我没有完全得到右值参考。
答案 0 :(得分:16)
我构建了一个简短的示例,当push_back
替换为emplace_back
时,实际上无法编译:
#include <vector>
struct S {
S(double) {}
private:
explicit S(int) {}
};
int main() {
std::vector<S>().push_back(0); // OK
std::vector<S>().emplace_back(0); // error!
}
对push_back
的调用需要将其参数0
从类型int
转换为类型S
。由于这是隐式转换,因此不考虑显式构造函数S::S(int)
,并调用S::S(double)
。另一方面,emplace_back
执行直接初始化,因此会考虑S::S(double)
和S::S(int)
。后者是一个更好的匹配,但它是private
,所以该程序是不正确的。
答案 1 :(得分:2)
是的,您可以更改行为(不仅仅是避免复制构造函数调用),因为emplace_back
只能看到不完美转发的参数。
#include <iostream>
#include <vector>
using namespace std;
struct Arg { Arg( int ) {} };
struct S
{
S( Arg ) { cout << "S(int)" << endl; }
S( void* ) { cout << "S(void*)" << endl; }
};
auto main()
-> int
{
vector<S>().ADD( 0 );
}
示例构建:
[H:\dev\test\0011] > g++ foo.cpp -D ADD=emplace_back && a S(int) [H:\dev\test\0011] > g++ foo.cpp -D ADD=push_back && a S(void*) [H:\dev\test\0011] > _
附录:正如Brian Bi在his answer中指出的那样,另一个可能导致不同行为的差异是push_back
调用涉及隐式转换为{{1} }},它忽略T
构造函数和转换运算符,而explicit
使用直接初始化,它也会考虑emplace_back
构造函数和转换运算符。
答案 2 :(得分:2)
emplace
版本在异常情况下不会在所有中创建所需类型的对象。这可能会导致错误。
考虑以下示例,为简单起见使用std::vector
(假设uptr
的行为类似于std::unique_ptr
,但构造函数不是显式的):
std::vector<uptr<T>> vec;
vec.push_back(new T());
它是异常安全的。创建临时uptr<T>
以传递给push_back
,T
移动到向量中。如果向量的重新分配失败,则分配的std::vector<uptr<T>> vec;
vec.emplace_back(new T());
仍归智能指针所有,该指针正确删除它。
比较:
emplace_back
ptr
不允许创建临时对象。 T
将在向量中就地创建一次。如果重新分配失败,则没有就地创建的位置,也不会创建智能指针。 std::vector<std::unique_ptr<T>> vec;
vec.push_back(make_unique<T>());
将被泄露。
当然,最好的选择是:
{{1}}
等同于第一个,但使智能指针创建显式。
答案 3 :(得分:1)
如果你在向量中保存的对象的复制构造函数中没有疯狂的副作用,那么没有。
引入了 emplace_back
来优化不必要的复制和移动。
答案 4 :(得分:1)
假设可以从braced-initializer初始化用户定义的类。 e.g。
Maven
然后
struct S {
int value;
};
std::vector::emplace_back
是模板函数,但std::vector::push_back
是非模板函数。使用支撑初始值设定项std::vector<S> v;
v.push_back({0}); // fine
v.emplace_back({0}); // template type deduction fails
会失败,因为template argument deduction失败。
非推断的上下文
6)参数P,其A是braced-init-list,但P不是
std::vector::emplace_back
或是对1的引用:
答案 5 :(得分:0)
int main() {
std::vector<S>().push_back(0);
std::vector<S>().emplace_back(0);
}
在emplace_back中给出构造函数,即上面的代码将是这样的
int main() {
std::vector<S>().push_back(0);
std::vector<S>().emplace_back(S(0));
}