emplace_back() 与 push_back 插入 std::vector 时

emplace_back() vs push_back when inserting a pair into std::vector

提问人:24n8 提问时间:12/23/2018 最后编辑:max6624n8 更新时间:4/9/2019 访问量:6840

问:

我定义了以下内容

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()

C++ C++11 STL 推送 emplace

评论

5赞 max66 12/23/2018
工程my_vec.emplace_back(1,2);
0赞 24n8 12/23/2018
为什么使用不起作用?({1,2})

答:

12赞 bolov 12/23/2018 #1

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

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

当你这样称呼它时:你是用一个参数来称呼它,即 并且无法推断。那是因为语言是如何演变的。在 C++ 中没有类型。它是一个括起来的 init-list,可用于某些类型的初始化,但都需要知道初始化的类型。这就是工作的原因,因为 的类型是 know 并且具有匹配的构造函数。emplace_back({1, 2}){1, 2}Args{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_backTstd::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

5赞 6502 12/23/2018 #2

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

语法

{1, 2}

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

通常在 C++ 中,你有一个表达式(例如 ),并且表达式有一个推导类型......例如,如果是一个变量,则表达式的类型将是由于隐式转换→以及加法的工作原理。x + 1.2xintdoubleintdouble

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

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

文本本身的意义不足以被编译(根据使用位置的不同,它可能意味着不同的东西)。{1, 2}

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

2) 接受构造函数参数emplace_back

emplace_back被设计为能够直接在容器的最终位置内构建对象......预期的参数是构造函数的参数,这样做是为了避免创建一个临时对象,只是为了能够为最终目标制作一个副本,然后把它扔掉。 因此,的预期参数是 和 ...没有单一的东西,因为不构建一个临时的单一东西正是设计的原因。emplace_back12emplace_back

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

后果

总而言之:不能使用,因为它可以接受任何东西(所以没有提供足够的“上下文”),而且语法没有足够的意义。 相反,可以接受它,因为它需要特定的类型,这为解释语法提供了足够的上下文。 这是一个简化的解释,但像往常一样,C++ 朝着一个更加复杂和特殊情况的方向发展,所以我可以理解为什么事情对你来说不清楚。emplace_back{1, 2}push_back{1, 2}

然而,关键的一点是,这并不意味着要接受一个完整的对象......为此,.如果要传递 CONSTRUCTOR PARAMETERS 以在容器中构建最终对象,则应使用新构造。emplace_backpush_backemplace_back