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

Iam*_*non 3 c++ stl push c++11 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
Run Code Online (Sandbox Code Playgroud)

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

bol*_*lov 5

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

template< class... Args >
reference emplace_back( Args&&... args );
Run Code Online (Sandbox Code Playgroud)

当你调用它是这样的:emplace_back({1, 2})你是一个参数,即调用它{1, 2}Args不能推断。那是因为语言的发展。在C ++ {1, 2}中没有类型。它是一个用括号括起来的init-list,可以在某些类型的初始化中使用,但是都需要知道初始化的类型。这就是为什么temp_pair = {1,2};起作用的原因,因为的类型temp_pair是已知的,并且具有构造函数match (int, int)

无论如何emplace_back,不应该那样使用,而是这样:

my_vec.emplace_back(1, 2);
Run Code Online (Sandbox Code Playgroud)

另外请注意,即使这些工作:

my_vec.emplace_back(std::pair<int, int>{1, 2});
my_vec.emplace_back(temp_pair);   
Run Code Online (Sandbox Code Playgroud)

他们不应该被使用。与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 :(
}
Run Code Online (Sandbox Code Playgroud)

除非您为添加了构造函数,否则此方法将无效Agg。这被认为是标准中的一个开放缺陷,但是不幸的是,他们无法找到一个好的解决方案。问题在于大括号init初始化的工作方式,如果在通用代码中使用它,则可能会错过一些构造函数。有关所有细节的细节,请查看以下精彩文章:为什么可以对花格初始化结构进行花括号初始化,但不能使用与花括号初始化中相同的参数列表来放置它?


650*_*502 5

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

语法

{1, 2}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

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

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

2)emplace_back接受构造函数参数

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

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

结果

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

然而,关键点是这emplace_back并不意味着要使用完整的对象...用于该用途push_backemplace_back当您想要传递 CONSTRUCTOR PARAMETERS 以在容器中构建最终对象时,应使用新构造。