0

From what I've read regarding emplace vs push for STL containers, the benefit of emplace over push is only evident when the object you're emplacing is an r-value and a non-POD type. Is this the only situation in which emplace is faster?

user5965026
  • 465
  • 5
  • 16
  • `emplace` is useful because you don't have to construct and then `push_back` a copy of the object. `emplace` can take the values the constructor would take and construct the object in-place saving a copy. See the description and examples here: https://en.cppreference.com/w/cpp/container/vector/emplace_back – Richard Critten Mar 09 '21 at 17:35
  • @RichardCritten Yes, I do understand that, and isn't that aligned with what I alluded to in the OP? I'm trying to figure out if there's some other benefit to emplace that I'm not privy to. – user5965026 Mar 09 '21 at 17:38
  • 1
    Nope if you are emplacing an rvalue then you must, by definition, have already constructed a temporary object of the type to be emplaced. This does not avoid the necessary copy or move. – Richard Critten Mar 09 '21 at 17:41
  • @RichardCritten Doesn't it avoid the copy because in this case it would use move semantics for r-values? – user5965026 Mar 09 '21 at 17:43
  • `ComplexClass temp{args...}; container.push_back(temp);` is often considered inferior to `container.push_back(args...);`, despite the fact that `temp` is an l-value. – Nathan Pierson Mar 09 '21 at 17:43
  • @NathanPierson: Note that, in your example, because you used list-initialization, the equivalent `emplace` version may not result in building the same object. – Nicol Bolas Mar 09 '21 at 17:50
  • Good point. Also I just noticed that I wrote `push_back` twice when the second time is supposed to be `emplace_back`, oops. – Nathan Pierson Mar 09 '21 at 17:52

1 Answers1

3

The emplace functions are meant for inserting into a container an object that does not exist yet. That is, at the site where you're considering using emplace, you should have the parameters to an object's constructor, and thus you are choosing between insert/push_back(Typename(...)) and emplace/_back(...), where ... are the arguments to the constructor.

If the object you're dealing with already exists (whether an lvalue or an rvalue), then which you choose doesn't actually matter. You'll have to copy/move from it, and both alternatives can handle those cases just fine.

The utility of in-place construction really has nothing to do with whether the type is trivial, POD, or move-only. It's all about constructing the object in-place. And if you have the ability to do in-place construction (ie: you don't have an object yet), you should prefer to do that.

Given some container of BigObject, emplace_back(args) will be better than push_back(BigObject(args)). This is not a function of BigObject's movement capabilities or triviality or being POD. This is a function of BigObject being big; that is, sizeof(BigObject) is significant. As such, the cost of copying/moving could be significant, so constructing it in-place makes more sense than passing it as a parameter.

And no, guaranteed elision doesn't help. push_back gives a name to its parameter, so it stops being a prvalue inside of push_back. As such, it must materialize the temporary, then copy/move it into the container's object.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • "If the object you're dealing with already exists (whether an lvalue or an rvalue), then which you choose doesn't actually matter." Right. Unfortunately, that's not the full truth about the relationship between `insert` vs. `emplace` for `std::vector` at least. There are some relevant differences in detail especially for the case of moving vector-content into itself. – Secundi Mar 09 '21 at 19:31
  • @Secundi: care to explain what that is? – Nicol Bolas Mar 09 '21 at 19:41
  • From cppreference: emplace: "The element is constructed through std::allocator_traits::construct, which typically uses placement-new to construct the element in-place at a location provided by the container. However, if the required location has been occupied by an existing element, the inserted element is constructed at another location at first, and then move assigned into the required location.". That's not the case for insert.. Also see for instance this defect report https://cplusplus.github.io/LWG/issue2164 – Secundi Mar 09 '21 at 19:58
  • + there are further asymmetries for instance for the effective public interface in comparison to `push_back` vs. `emplace_back` due to iterator-involvement. – Secundi Mar 09 '21 at 20:00