emplace_back()与push_back()在将一对键值对插入std::vector时的区别

7

我定义了以下内容

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进行编译。第三行存在问题,但我认为您可以在任何使用push_back()的地方使用emplace_back(),但这显然是错误的。为什么第三行不能正常工作?


5
这段代码的含义是向“my_vec”这个容器中添加一个元素,该元素的值为1和2。使用的方法是“emplace_back”。 - max66
为什么使用({1,2})不起作用? - 24n8
2个回答

12

emplace_back 接受可变参数包作为参数:

template< class... Args >
reference emplace_back( Args&&... args );
当您以以下方式调用时:emplace_back({1, 2}),您正在使用一个参数即{1, 2}进行调用,而Args无法被推断出。这是由于语言的演变方式造成的。在C++中,{1, 2}没有类型。它是一个括号括起来的初始化列表,可以用于某些类型的初始化,但是所有初始化都要求已知初始化的类型。这就是为什么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。

但我认为你可以在任何你有push_back的地方使用emplace_back

在大多数情况下,这是正确的。至少这是意图。而且你确实可以在你的情况下使用它。只需要稍微调整一下语法。所以,代替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添加构造函数,这段代码是无法正常工作的。这被认为是标准中的一个缺陷,但不幸的是他们找不到一个好的解决方案。问题出在花括号初始化的方式上,如果在通用代码中使用它,你可能会漏掉一些构造函数。想了解更多细节,请查看这篇优秀的文章:为什么可以用花括号初始化聚合结构体,但不能使用与花括号初始化相同的参数列表进行插入?


5

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

语法错误

{1, 2}

与C++中的其他内容相比,{1, 2}非常“奇怪”。

通常在C++中,您有一个表达式(例如x + 1.2),表达式具有推断类型...例如,如果x是一个int变量,则由于隐式转换intdouble和加法运算的工作原理,表达式的类型将为double

现在回到{1, 2}:这很“奇怪”,因为尽管看起来像表达式,但实际上不是...它只是语法,其含义将取决于其使用位置。

在某种意义上,这里的键入方式与大多数C++位置相反:通常在C++中是“内”→“外”(类型“出现”于组件中),但在这里是“外”→“内”(类型被“注入”到组件中)。

文本{1, 2}本身并不足以编译(它的含义可能因使用位置而异)。

所有这些归结为一个事实,即{1, 2}不能像表达式一样精确地使用,即使规则经过精心设计,试图让您认为它可以。

2)emplace_back接受构造函数参数

emplace_back的设计目的是能够直接在容器的最终位置内部构建对象...期望的参数是构造函数的参数,这样做是为了避免创建临时对象,仅为了能够复制到最终位置并将其丢弃。 因此,emplace_back的预期参数是12...不是单个内容,因为不构建临时单个内容正是emplace_back的设计目的。

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

结果

总之:emplace_back不能使用{1, 2},因为它可以接受任何内容(因此没有提供足够的“上下文”),而且该语法没有足够的含义。push_back可以接受它,因为它期望特定类型,这为解释语法{1, 2}提供了足够的上下文。 这是一个简化的解释,但通常情况下,C++走向了更复杂的解析和特殊情况的方向,因此我可以理解为什么事情对您来说不太清楚。

然而,关键点在于emplace_back并不是用来接受完整对象的...对于这种情况请使用push_back。当您想要传递构造函数参数以建立容器中最终对象时,请使用新的emplace_back构造函数。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接