![](/img/trans.png)
[英]emplace_back() vs push_back when inserting a pair into std::vector
[英]push_back vs emplace_back to a std::vector<std::string>
此处和此处存在类似的问题,但我认为我们还没有得到明确的答案,然后我重新提出问题。
C++11 标准有两种在 vector 末尾添加新元素的方法,它们是std::vector::push_back
和std::vector::emplace_back
。
它们之间的区别在于std::vector::emplace_back
构造对象,而std::vector::push_back
基本上复制对象(或原始类型)或将其移动到 vector 的末尾。
然后std::vector::push_back
看起来是将基本类型添加到std::vector
中的更好选择。 例如:
std::vector<int> vVec;
vVec.push_back(10);
int iVar 30;
vVec.push_back(iVar);
但是当我们谈论对象时,我并不是很清楚。 让我们以 std::string 的向量为例。 如果我想添加一个文字字符串,我应该使用std::vector::emplace_back
,当我想复制或移动一个字符串对象时,我应该使用std::vector::push_back
?
为了更清楚地说明我在问什么,让我们看几个场景:
第一种情况:
std::vector<string> vVec;
std::string sTest("1st scenario");
vVec.push_back(sTest);
vVec.emplace_back(sTest);
Push_back 复制sTest
并关联到 vVec 末尾的新元素,同时emplace_back
在vVec
末尾创建字符串对象并将vVec
的内容sTest
到其中。 在这种情况下哪个更有效或者无关紧要?
第二种情况:
std::vector<string> vVec;
std::string sTest1("2st scenario");
std::string sTest2("2st scenario");
vVec.push_back(move(sTest1));
vVec.emplace_back(move(sTest2));
Push_back 已准备好移动语义,但是emplace_back
会发生什么? 在这种情况下哪个更有效?
第三种情况:
std::vector<string> vVec;
std::string sTest("3st scenario");
vVec.push_back(sTest.substr(4,4));
vVec.emplace_back(sTest.substr(4,4));
因为 std::string::substr 返回另一个 std::string 我们遇到与第二种情况相同的情况?
结论
如果std::vector::emplace_back
仅在涉及文字(就地构造)时比std::vector::push_back
更有效,那么使用它真的合理吗? 如果是的话,你能给我一些例子吗?
重要的是要注意std::vector::emplace_back
是在 c++11 中而不是在 c++0x 中实现的,那么我认为它有充分的理由存在。
它在您的三种场景中没有太大区别,因为这两个函数将在场景 1 中调用一个复制构造函数,在场景 2 或 3 中调用一个移动构造函数。
但是如果你想构造一个包含 10 个'x'
字符的字符串怎么办? 在这种情况下,您的选择是
vVec.push_back(std::string(10, 'x'));
vVec.emplace_back(10, 'x');
在这种情况下, push_back
涉及调用自定义string
构造函数,然后调用移动构造函数,但emplace_back
直接调用自定义string
构造函数,从而节省了对移动构造函数的调用。
std::string
的移动构造函数可能不是什么大问题,但是当对象没有有效的移动构造函数时, emplace
函数可以节省时间,并且即使类类型由于某种原因具有已删除的移动构造函数也可以使用。 (好吧,如果你删除移动和复制构造函数, std::vector
会不高兴,但其他容器可以接受。)
首先让我们澄清一下:
emplace
系列接受构造函数的参数,而不是对象本身。
然后它用这些参数就地构造对象,它永远不会构造临时对象然后将其复制或移动到容器中。
也就是说,将相同类型的对象作为参数正是复制和移动构造所做的,这就是为什么在您的示例中,它们调用相同的构造函数:它们是用已经构造string
调用的。
emplace
和push
完全不同的地方是使用不是对象本身的构造函数参数调用emplace
: emplace
不需要构造一个临时的然后复制到容器中。
std::vector<std::string> strvec;
strvec.emplace_back("abc") //calls string(const char*)
strvec.push_back("abc") //is equivalent to...
strvec.push_back(string("abc")) //which calls string(const char*) then string(string&&)
我花了一段时间才真正理解使用 std::vector::emplace 的优势是什么,正如 aschepler 所说。
我发现更好的使用场景是当我们有自己的类在构造时接收一些数据。
为了更清楚,让我们假设我们有:
然后我们可以有这样一行:
vVec.emplace(get1stElem(), get2ndElem(), get3rdElem());
然后 std::vector::emplace 将比 std::vector::push_back 更有效地就地构造 MyObject。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.