6

我定义了以下

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

4

2 回答 2

10

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<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 初始化的工作方式,如果您在通用代码中使用它,您可能会错过一些构造函数。有关所有细节,请查看这篇精彩的帖子:为什么聚合结构可以进行大括号初始化,但不能使用与大括号初始化中相同的参数列表进行放置?

于 2018-12-23T11:11:47.740 回答
5

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

语法

{1, 2}

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

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

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

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

文本{1, 2}本身并不足以编译(它可能意味着不同的东西,具体取决于它的使用位置)。

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

2)emplace_back接受构造函数参数

emplace_back旨在能够直接在容器的最终位置内构建对象...预期参数是构造函数的参数,这样做是为了避免创建临时对象只是为了能够为最终目的地制作副本,并且然后把它扔掉。因此,期望的参数emplace_backare 1and 2... not a single thing 因为不构建临时的单一事物正是emplace_back设计的原因。

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

结果

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

然而,关键点是这emplace_back并不是为了取一个完整的对象……为了那个用途push_backemplace_back当您想要传递 CONSTRUCTOR PARAMETERS 以在容器中构建最终对象时,应使用新构造。

于 2018-12-23T11:43:01.410 回答