将一对插入std :: vector时emplace_back()vs push_back

时间:2018-12-23 02:53:53

标签: c++ c++11 stl push emplace

我定义了以下内容

std::vector<std::pair<int,int> > my_vec;
my_vec.push_back( {1,2} ); //this works
my_vec.emplace_back( {1,2} ); // this doesn't work
std::pair<int,int> temp_pair = {1,2}; 
my_vec.emplace_back( temp_pair );         //this works

我正在使用c ++ 11进行编译。第三行是有问题的,但是我认为您可以在拥有emplace_back()的任何地方使用push_back(),但这显然是错误的。为什么第三行不起作用?

2 个答案:

答案 0 :(得分:4)

emplace_back以可变参数包作为参数:

template< class... Args >
reference emplace_back( Args&&... args );

当您这样称呼它时:emplace_back({1, 2})是使用一个参数(即{1, 2})来调用它的,因此无法推论Args。那是因为语言的发展。在C ++中,{1, 2}没有类型。它是一个用大括号括起来的init-list,可以在某些类型的初始化中使用,但都需要知道初始化的类型。这就是temp_pair = {1,2};起作用的原因,因为temp_pair的类型是已知的,并且具有与(int, int)匹配的构造函数。

不应该这样使用emplace_back,而是这样:

my_vec.emplace_back(1, 2);

也请注意,即使这些方法有效:

my_vec.emplace_back(std::pair<int, int>{1, 2});
my_vec.emplace_back(temp_pair);   

不应该使用它们。与push_back相比,它们没有任何优势。 emplace_back的全部要点是避免创建临时的T。上面的调用全部创建了临时std::pair<int, int>


  

但是我认为您可以在任何有空的地方使用emplace_back()   push_back()

在大多数情况下,这是正确的。至少那是意图。而且您确实可以在自己的Cese中使用它。您只需要稍微调整一下语法即可。因此,您可以使用push_back({1, 2})代替emplace_back(1, 2)

不幸的是,您无法使用emplace_back:聚合。

struct Agg
{
    int a, b;
};

auto test()
{
    Agg a{1, 2}; // ok, aggregate initialization

    std::vector<Agg> v;
    v.emplace_back(1, 2); // doesn't work :(
}

除非您为Agg添加一个构造函数,否则这将不起作用。这被认为是标准中的一个开放缺陷,但是不幸的是,他们无法找到一个好的解决方案。问题在于大括号init初始化的工作方式,如果在通用代码中使用它,则可能会错过一些构造函数。有关所有细节的信息,请查看以下精彩文章:Why can an aggreggate struct be brace-initialized, but not emplaced using the same list of arguments as in the brace initialization?

答案 1 :(得分:3)

1){1, 2}不是表达式

语法

{1, 2}
与C ++中的其他内容相比,

非常“奇怪”。

通常在C ++中,您有一个表达式(例如x + 1.2),并且该表达式具有推导的类型……例如,如果xint变量,则表达式的类型将之所以成为double是因为隐式转换intdouble以及加法的工作原理。

现在回到{1, 2}:这是“奇怪的”,因为尽管看起来像一个表达式,它却不是……只是语法,其含义取决于使用的地方。

从某种意义上说,在这里键入将与大多数C ++位置相反:通常在C ++中,它是“ in”→“ out”(类型从组件中“出现”),但是这里是“ out”→“ in”(类型是在组件中“注入”。

{1, 2}文本本身的含义不足以进行编译(根据使用位置的不同,含义可能有所不同)。

所有这些可以归结为一个事实,即{1, 2}不能像表达式一样精确地使用,即使规则经过精心设计以欺骗您也是如此。

2)emplace_back接受构造函数参数

emplace_back旨在能够直接在容器的最终位置内构建对象...预期的参数是构造函数的参数,这样做是为了避免仅为了能够创建临时对象复制最终目的地,然后扔掉。 因此,emplace_back的预期参数是12 ...不是一件事,因为不构建临时的一件事正是emplace_back为其设计的原因。 / p>

您可以传递emplace_back实例,因为所包含的类型具有复制构造函数,并且该实例被视为复制(移动)构造函数的参数,而不是要复制(移动)到目标的对象({ {1}}期望)。在这种情况下执行的操作相同,但是观点不同。

后果

总结:push_back无法使用emplace_back,因为它可以接受任何内容(因此不能提供足够的“上下文”),并且该语法没有足够的含义。 {1, 2}可以接受它,因为它需要特定的类型,并且可以提供足够的上下文来解释语法push_back。 这是一个简化的解释,但是像往常一样,C ++朝着更多地分析复杂性和特殊情况的方向发展,因此我可以理解为什么事情对您不明确。

然而,关键点是{1, 2}并不意味着要拿走完整的物体……为此emplace_back。当您要通过构造函数参数以在容器中构建最终对象时,应使用新的push_back构造。