5

I am currently reading Scott Meyer's "Effective Modern C++". In Item 42, he claims that e.g. an std::vector::emplace_back is usually, but not always, as fast as or even faster than using push_back. He lists three conditions under which it should be at least as fast, but does not provide a counterexample in the case where these conditions are not all satisfied. Can someone provide me with an example where using emplace_back would be expected to result in strictly worse performance than using push_back?

edmz
  • 8,220
  • 2
  • 26
  • 45
Fabian Meumertzheim
  • 1,569
  • 1
  • 11
  • 21
  • It's not really the same thing, but you can't use `emplace_back` for a braced initializer (e.g., calling a list constructor like `vecOfVecs.emplace_back({1, 2, 3});`), so I guess that would make it not as fast as `push_back`. – chris Oct 05 '15 at 05:20
  • 8
    `He lists three conditions` which are? – user657267 Oct 05 '15 at 06:52
  • 1
    It would be a really obscure and odd circumstance if `emplace_back` is actually *slower*. It's normally same or better. – sp2danny Oct 05 '15 at 13:47
  • Scott Meyers refers to this exact question in his keynote talk @ Meeting C++ 2014 [here](https://www.youtube.com/watch?v=smqT9Io_bKo&t=18m25s). Enjoy. – Amir Kirsh Feb 16 '21 at 17:17

3 Answers3

4

It depends on what you mean by "emplace_back is slower than push_back". Considering class that is expensive to construct and cheap to copy, for example class with copy-on-write behavior, or class representing hash value:

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

Then, (1) will be indeed faster than (2). However, such comparision is not fair. When comparing performance, costs of constructors involved should be factored in.

2

Silly example:

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

would probably be faster than

std::vector<always_throws_on_construction> vec;
if(vec.size() == vec.capacity())
{
    vec.emplace_back();
}
Mike Vine
  • 9,468
  • 25
  • 44
  • What's the rationale for that guess? – edmz Oct 05 '15 at 12:41
  • @black In the push back case, you throw an exception before you run any vector code. In the emplace back case you dynamically expand the vector to be able to handle the new element and then throw. Like I said, its a silly example. [edited to be clear an exception is thrown] – Mike Vine Oct 05 '15 at 13:16
  • Oh yeah. For some reason I thought you were saying the opposite, that's why I asked. – edmz Oct 05 '15 at 13:36
1

Essentially this boils down to std implementations. Theoretically, emplace should always be as fast or faster, except the reality is that no standard library implementation takes full advantage of that.

He gave a talk on this exact issue a few years ago: https://www.youtube.com/watch?t=3427&v=smqT9Io_bKo

Check out the first 1 hour of the talk for a more detailed explanation. The Q&A at the end of the talk is relevant as well.

Elenchev
  • 308
  • 1
  • 8