我定义了以下内容
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()
,但这显然是错误的。为什么第三行不起作用?
答案 0 :(得分:4)
emplace_back
以可变参数包作为参数:
template< class... Args > reference emplace_back( Args&&... args );
当您这样称呼它时:emplace_back({1, 2})
是使用一个参数(即{1, 2}
)来调用它的,因此无法推论Args
。那是因为语言的发展。在C ++中,{1, 2}
没有类型。它是一个用大括号括起来的init-list,可以在某些类型的初始化中使用,但都需要知道初始化的类型。这就是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初始化的工作方式,如果在通用代码中使用它,则可能会错过一些构造函数。有关所有细节的信息,请查看以下精彩文章:Why can an aggreggate struct be brace-initialized, but not emplaced using the same list of arguments as in the brace initialization?
答案 1 :(得分:3)
{1, 2}
不是表达式语法
{1, 2}
与C ++中的其他内容相比,非常“奇怪”。
通常在C ++中,您有一个表达式(例如x + 1.2
),并且该表达式具有推导的类型……例如,如果x
是int
变量,则表达式的类型将之所以成为double
是因为隐式转换int
→double
以及加法的工作原理。
现在回到{1, 2}
:这是“奇怪的”,因为尽管看起来像一个表达式,它却不是……只是语法,其含义取决于使用的地方。
从某种意义上说,在这里键入将与大多数C ++位置相反:通常在C ++中,它是“ in”→“ out”(类型从组件中“出现”),但是这里是“ out”→“ in”(类型是在组件中“注入”。
{1, 2}
文本本身的含义不足以进行编译(根据使用位置的不同,含义可能有所不同)。
所有这些可以归结为一个事实,即{1, 2}
不能像表达式一样精确地使用,即使规则经过精心设计以欺骗您也是如此。
emplace_back
接受构造函数参数 emplace_back
旨在能够直接在容器的最终位置内构建对象...预期的参数是构造函数的参数,这样做是为了避免仅为了能够创建临时对象复制最终目的地,然后扔掉。
因此,emplace_back
的预期参数是1
和2
...不是一件事,因为不构建临时的一件事正是emplace_back
为其设计的原因。 / p>
您可以传递emplace_back
实例,因为所包含的类型具有复制构造函数,并且该实例被视为复制(移动)构造函数的参数,而不是要复制(移动)到目标的对象({ {1}}期望)。在这种情况下执行的操作相同,但是观点不同。
总结:push_back
无法使用emplace_back
,因为它可以接受任何内容(因此不能提供足够的“上下文”),并且该语法没有足够的含义。 {1, 2}
可以接受它,因为它需要特定的类型,并且可以提供足够的上下文来解释语法push_back
。
这是一个简化的解释,但是像往常一样,C ++朝着更多地分析复杂性和特殊情况的方向发展,因此我可以理解为什么事情对您不明确。
然而,关键点是{1, 2}
并不意味着要拿走完整的物体……为此emplace_back
。当您要通过构造函数参数以在容器中构建最终对象时,应使用新的push_back
构造。