是否有任何使用emplace_back替换push_back不正确的情况?

时间:2014-02-27 21:02:05

标签: c++ c++11 vector c++03

我可以通过用std::vector::push_back替换emplace_back并用C ++ 11编译器编译来破坏有效的C ++ 03程序吗?从阅读emplace_back参考我收集它不应该发生,但我承认我没有完全得到右值参考。

6 个答案:

答案 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_backT移动到向量中。如果向量的重新分配失败,则分配的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的引用:

LIVE

答案 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)); 
}