【问题标题】:emplace_back() vs push_back when inserting a pair into std::vector将一对插入 std::vector 时 emplace_back() 与 push_back
【发布时间】:2019-05-22 21:00:19
【问题描述】:

我定义了以下

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(),但这显然是错误的。为什么第三行不起作用?

【问题讨论】:

  • 作品my_vec.emplace_back(1,2);
  • 为什么使用({1,2}) 不起作用?

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


【解决方案1】:

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&lt;int, int&gt;


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

大部分情况下是正确的。至少这是本意。你确实可以在你的cese中使用它。你只需要稍微调整一下语法。所以你可以用emplace_back(1, 2)代替push_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?

【讨论】:

    【解决方案2】:

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

    语法

    {1, 2}
    

    与 C++ 中的其他东西相比非常“奇怪”。

    通常在 C++ 中你有一个表达式(例如x + 1.2)并且该表达式有一个推导的类型......例如,如果x 是一个int 变量,那么表达式的类型将是double,因为隐式转换intdouble 以及加法的工作原理。

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

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

    文本{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。当您想要传递 CONSTRUCTOR PARAMETERS 以在容器中构建最终对象时,应使用新的 emplace_back 构造。

    【讨论】:

      猜你喜欢
      • 2015-01-15
      • 2017-09-21
      • 1970-01-01
      • 2021-06-02
      • 2019-12-22
      • 1970-01-01
      • 2018-05-31
      • 2017-01-12
      • 1970-01-01
      相关资源
      最近更新 更多