std::vector::emplace_back 比 std::vector::push_back 慢的示例是什么?

5
我正在阅读Scott Meyer的《Effective Modern C++》。在第42条中,他声称例如std::vector::emplace_back通常情况下至少与使用push_back一样快甚至更快。他列出了三个条件,在这些条件得到满足的情况下,它应该至少和push_back一样快,但是在这些条件不全都得到满足的情况下,他没有提供反例。 有人可以给我一个例子,使用emplace_back预计性能明显比使用push_back差吗?

虽然它们并不完全相同,但你不能使用emplace_back来进行花括号初始化(例如,调用列表构造函数,如vecOfVecs.emplace_back({1, 2, 3});),所以我猜这会使它不如push_back快。 - chris
8
他列举了三个条件是什么? - user657267
1
如果 emplace_back 实际上比较慢,那将是非常罕见和奇怪的情况。通常情况下,它的速度要么相同,要么更快。 - sp2danny
Scott Meyers在他的主题演讲@ Meeting C++ 2014 [here](https://www.youtube.com/watch?v=smqT9Io_bKo&t=18m25s)中提到了这个问题。享受。 - Amir Kirsh
3个回答

4
这取决于你对“emplace_backpush_back慢”的理解。考虑到构造代价高但拷贝代价低的类,例如具有写时复制行为或表示哈希值的类:
class Hash {
    public:
    int value;
    Hash(const char *data) : value(very_expensive_hash_function(data)) {} // expensive
    Hash(const Hash &other) : value(other.value) {} // cheap
};
Hash h(foo);
std::vector<Hash> v;

v.push_back(h);        // 1
v.emplace_back("foo"); // 2

然后,(1)将比(2)更快。然而,这样的比较并不公平。在比较性能时,应考虑构造函数的成本。

2

一个愚蠢的例子:

std::vector<always_throws_on_construction> vec;
if(vec.size() == vec.capacity())
{
    vec.push_back(always_throws_on_construction());
}

可能比...更快

std::vector<always_throws_on_construction> vec;
if(vec.size() == vec.capacity())
{
    vec.emplace_back();
}

那个猜测的理由是什么? - edmz
@black 在 push back 的情况下,在运行任何向量代码之前,您会抛出异常。在 emplace back 的情况下,您会动态扩展向量以处理新元素,然后再抛出异常。就像我说的那样,这只是一个愚蠢的例子。【已编辑为明确抛出异常】 - Mike Vine
哦,是的。出于某种原因,我以为你说的是相反的,所以我才问的。 - edmz

1
基本上,这归结为标准实现。理论上,emplace 应该始终更快,除非现实是没有标准库实现能够充分利用它。
他在几年前就这个问题发表了演讲: https://www.youtube.com/watch?t=3427&v=smqT9Io_bKo 查看演讲的前1小时以获取更详细的解释。演讲结束时的问答环节也很相关。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接