> Or maybe use whatever the fuck you want and let's other to decide for themselves. Often people even do not have a choice.
And you know why they don't have the choice to not use C++? Because someone else made a choice to use C++ and so here we are. That's the paradox of having a freedom in chosing the language: only the first contributor has that freedom, everyone else either has to accept their decision, or leave.
Don't use Go, use C#. The same code is even more terse:
List<T> lst = new();
lst.Add(obj);
Done. Sure go is nice if you only care about being hip and trendy, but C# is better in more situations than you'd think. Why bother with new languages and absurd syntax when C# has been around for decades and has perfectly clear syntax that spells out exactly what you want in simple English?
Don't be an asshole. "Just use today's trendy language instead of crusty old C++" makes you an asshole. Stop it.
This is either making a copy or storing things as pointers with an indirection penalty, how do you do in go if you want to add an object to a container without making a copy or without indirection
I’m not interested in who you’re replying to, but your question seems to imply there’s some way to add data to a container without already having some copy. That’s only possible if the container (and it’s data) is static.
The discrimination is the penalty of copying a stack-value vs some GC’d/managed memory. Then if you compare those scenarios the semantics lead to more interesting topics for debate.
> “some way to add data to a container without already having some copy”
That’s what C++ vector emplace_back does. It allocates the memory if needed, then constructs the object in place using the provided arguments. No need for a copy.
I'm interested in the semantics of "if needed" and "constructs in place."
I'm confused as to how you could initialize dynamic memory without a copy. I feel like initializing dynamic memory _is_ copying (memcpy), unless we're talking about some sort of fully constexpr thing.
which directly creates the object and calls my_type::my_type(int) constructor in the std::vector's memory storage. The only output you'll see will be
int ctor
The inner std::string will also be initialized directly in the right memory position, at no point there will be a copy of, say, 10000 'A' characters.
In the second case, first you construct my_type on the stack of the calling code so you get a first call to
my_type::my_type(int)
Then my_type is moved (or copied, if it didn't have move constructors): std::vector's implementation does pretty much the same thing, but the result is two constructions instead of one:
So emplace_back() exists to forward the contructor's arguments to the place where the object can be constructed in-place while push_back() takes an already constructed object and (hopefully) moves it to where its needed.
You know, maybe forwarding the arguments is the wrong direction. Maybe there should have been the dual mechanism of back-propagating the ultimate destination of the object all the way back to its constructor instead.
That's sort of what happens with named return value optimization. You write this:
bar foo() {bar baz; ...; return baz;}
bar qux = foo();
'baz' is constructed directly in the space allocated for 'qux'.
Implementation notes: the complex value is never returned but rather the caller passes the address of a space where the return value should be constructed. Inside the function, the compiler notes that baz is returned and allocates it in the return value space.
This optimization is guaranteed to occur. Compilers which don't do this are nonconforming.
Actually, Rust would also really like an `emplace_back`, because it does have the issue that `.push_back(Widget::new(foo, bar, baz))` can end up creating a Widget local in the caller and then moving it into the Vec allocation. It doesn't happen as much with optimizations enabled, but it does happen in debug builds.
You might say "Big deal, it's just a tiny loss of performance to create a value and then copy it into its final place. Unlike C++ this is guaranteed to only do a copy of bytes, no complex code like a copy ctor. Who cares, especially if it's only noticeable in debug builds?" But it's not just a problem of performance. If it's `Box::new([0_u8; 10 * 1024 * 1024])`, then that 10 MiB array created in the caller's stack can end up blowing the caller's stack.
Rust did actually try to add emplace style APIs and a dedicated operator. It would've looked like `vec.place_back() <- Widget::new();` and it would've guaranteed that the generated code did not create a copy in the caller frame. It was never stabilized and instead eventually removed, because it was still not reliable enough to provide that guarantee after all.
The Box::new_zeroed family lets us sidestep this. I thought it was stabilised, but looks like not yet
Box::<u8>::new_zeroed_slice(10 * 1024 * 1024) says we want 10MiB of zero bytes as a MaybeUninit inside a box, we can then (since these are just bytes in our example) assume_init() since that's valid for our type although in the real world probably we'd actually store some actual data in the memory we've allocated - but it doesn't go on the stack.
If we're overwriting it all anyway there's also an adjacent set of uninit functions to skip the zero step, although of course the OS might be zeroing the page anyway.
Right, in the absence of placement-exprs, the alternatives are based around MaybeUninit. Box::new_zeroed() for the "boxed zero array" case, Box::new_uninit() + MaybeUninit::write() + Box::assume_init() for the "boxed arbitrary large value case", Vec::reserve() + Vec::spare_capacity_mut() + MaybeUninit::write() + Vec::set_len() for the "append to Vec" case, etc.
Re: the last paragraph, C++ has temporary materialization — space for temporary objects is not actually “allocated” until the object needs storage (commonly when it binds to a reference). The problem is that push_back takes by rvalue reference, which forces a materialization of the temporary. I don’t see a way around forcing materialization because taking a reference requires some uniform representation for the reference — the temporary could have been constructed by any one of its possibly many constructors. Forwarding arguments makes the parameters explicit, and the language already has support for this, so I don’t see a huge need for adding more magic.
Is this true with respect to the ABI? I get how a temporary is blown away at the end of a statement with a `;` if it wasn't bound to a reference that extends its lifetime, but wouldn't the code need to allocate a return slot regardless?
What I'm thinking of would be
```c++
struct MyBigClass { /* lots of members */ };
MyBigClass makeABigOne();
auto main() -> int
{
// We still need to construct a return slot here, even though we don't use the value, right?
makeABigOne();
}
```
Perhaps this is tangential, but I'm wondering if maybe I'm missing a subtly based on what you mean by "allocated" (in the abstract machine, or in the ABI).
C++ already has (named) return value optimization; I think the behavior you describe would happen when passing the result of a function to push_back/emplace_back.
> I recommend sticking with push_back for day-to-day use.
This Google tip of the week gives a good explanation (explicit vs implicit) for why to prefer push back vs emplace if you don't need the benefit of emplace: https://abseil.io/tips/112
In particular:
> Let me answer that question by asking another: what do these two lines of code do?
vec1.push_back(1<<20);
vec2.emplace_back(1<<20);
> The first line is quite straightforward: it adds the number 1048576 to the end of the vector. The second, however, is not so clear. Without knowing the type of the vector, we don’t know what constructor it’s invoking, so we can’t really say what that line is doing
push_back always sounds clunky and dissonant to me. Gotta wonder what was wrong with ".append" -- guess it wasn't applicable to stack-based containers or something.
ruby and js got shift and unshift from perl, which got shift from sh, where it's the only way to iterate over an array or other list. (also, sh only has one array)
in the old country, instead of shift and unshift, we said cdr and cons. we didn't have push and pop; if we wanted to mess with the backside of a list we had to reverse it first. but our code ran ten times as fast as ruby and had implicit undo
at some point don hopkins suggested, i think it was, eat and barf for, respectively, push and pop on the front end. the back end would then necessarily be boof and shit (not shift). betcha wouldn't forget which end those pertained to
Append to which side? Push and pop are terms of are for queues and stacks which containers can be and the back and front tell us which end of the container we mean
From a natural language perspective, I agree. In practice, however, I see far more container libraries use the terms: (1) add or append and (2) insert w/ index (instead of prepend). Do you know of any container libraries that use the term prepend? From time to time, I use it in a function for some kind of business logic, e.g., "prependMeetingInvite()".
JS dom manipulation has prepend and append. These always felt quite natural to me. While push back feels weird. The act of pushing feels out of place and I also think back and front may also be ambiguous.
Suffix is also a verb that means “to add something after another thing”. I think the verb prefix is the counterpart to the verb suffix, the noun prefix is the counterpart to the noun suffix, and the verb prepend is the counterpart to the verb append.
You can look at the earliest sources of the STL from 1994 pre-standardization and the names were already used back then. It's not entirely unsurprising considering even back then there was a vector and a deque.
A minor problem with "append" (and "prepend" for the front) is that, as far as I know, there isn't a really satisfying word for the reverse operation.
A search for the opposite of "append" suggests "remove", "subtract", "detach", "disconnect", "disjoin", "unfasten", "reduce", "diminish", and more. None of them really have the same "at the back" connotation that "append" has.
"Push" and "pop" are well known from stacks so it makes sense to use them for operations on one end of list-like containers. That still leaves the problem of what to call the similar operations on the other end.
I suppose we could sidestep that problem by only providing them at one end. So say push() adds to the back and pop() removes from the back.
If you want to operate at the front just reverse the list, do your push or pop, and then reverse the list again and let the optimizer deal with eliminating the two reverses. :-)
append is used for sequences. push_back is used for individual elements. Maybe not the most intuitive terminology, but there's value to separating the two ideas.
That argument is wrong, since push_back(1<<20) can invoke an implicit conversion to value_type, so by the same dint you also cannot say what that line is doing.
No, that's not what I'm saying. I have no idea where you saw me saying anything about those 2 lines or vectors in my sentences.
I am saying 1-arg constructors that aren't semantically "conversions" from that arg need to be be explicit, not implicit. If you make them implicit, that's your mistake; tools can catch that mistake statically.
Uh. I'll try to be more explicit. Let's say that someone in a library somewhere has made what you consider to be a mistake. Or they consider something to be a conversion but the constructor is not trivial. For the push_back construction, can you then say what the call does without knowing the types?
> Let's say that someone in a library somewhere has made what you consider to be a mistake. Or they consider something to be a conversion but the constructor is not trivial. For the push_back construction, can you then say what the call does without knowing the types?
I don't get the point of your question. You might as well ask: "Let's say someone in a library somewhere has defined their type's operator< to wipe your disk. If you see std::less<>()(a, b), can you say what the call does without knowing the types?"
To which the answer is... well, obviously, no. Same answer to your question.
There was very little in this conversation that was C++ specific, so it's kind of baffling that this conversation led you to that conclusion. But in general: yes, that's a good thing. Don't use C++ if you don't need to. It has too many sharp edges and it shouldn't be used unless it's really necessary.
Note this isn't really specific to Google, or really much of a "convention" (insofar as a convention is something you can reasonably avoid following). This is the only correct way to write C++, really -- implicit 1-arg constructors are specifically called "converting constructors" [1], precisely because their very purpose is to define implicit conversions. You don't define them unless implicit conversions make sense for your type. The standard library pays attention to this just as much -- that's why size_t isn't implicitly convertible to std::vector.
Of course (having learned most of my cpp at Google) I agree with you. However, the language default is incorrect (you have to opt out of implicit conversions rather than opt in) which is why I phrased it like that
> suppose you have a std::vector<std::vector<int>> and you want to append a number to the end of the first vector, but you accidentally forget the subscript. ... if you write my_vec.emplace_back(2<<20), the code will compile, and you won’t notice any problems until run-time.
How is this not caught by the type system? 2<<20 is clearly no way, whatsoever, compatible with a vector<int> type.
The compiler can't read your intention. Emplace means "forward these arguments to the constructor", so it forwards those arguments to the constructor (in this case, vector<int>).
With push back it would be caught by the compiler because you can't implicitly convert 2<<20 to a vector<int>
I personally consider that to be a bad recommendation and my understanding is that the author also objects to it. I would bet most people are unaware of how emplace actually works and for a reviewer it's more cognitive overhead too.
I'm unconvinced. emplace is inherently more complex and requires a complete understanding of the relevant constructors of the value type in the collection. push_back is much less surprising.
I'm not a professional C++ developer, but I'm obviously required to use the language every once in a while and I vastly prefer code that requires me to have to look at fewer implementations of potentially implicit and hidden things.
std::make_unique does the exact same kind of forwarding arguments to a constructor. Yeah it's important to know which constructor you're calling, but that's just a general fact, emplace_back isn't exceptional.
I kinda disagree with advise. Use emplace when you're passing args to the constructor. Saying to use push back for day to day use either says that you don't encounter this day to day (you probably do), or that you shouldn't bother understanding the difference. You should.
Constructors are just functions at the end of the day and they can do anything. Not really practical to decide when to invoke a constructor for the author in a language with semantics as complicated as C++. The convention there is to trust the author to be explicit with exactly where they want to point the gun and not try to move it from the author’s feet.
No, the signature of push_back accepts an object reference, while the signature of emplace_back accepts _arguments to forward to the element type’s constructor_. That means that pushback expects you to construct the object, while emplaceback wants to construct the object internally. It’s just that there’s usually a move constructor that accepts an object rvalue reference, so that case looks like it resembles the pushback API, if that makes sense....
In principle The Sufficiently Good compiler can do everything up-to the as-if rule. In practice most of the time the temporary object is optimized away. But if the object is very large or the copy has side effects, thing are harder or impossible and those are probably the times where you need the optimization the most.
The additional copy used by push back can be observable (because the copy constructor is just a function, which is very much allowed to have side effects). That means the compiler isn't allowed to optimize out the copy.
The article makes it more complex than it needs to be. push_back and emplace_back have different goals. The former works with existing objects, the latter gets used when you intend to construct a new one, right in the collection.
It happens to be that you can use emplace_back in place of push_back because copies and moves are just constructor overloads in C++. You shouldn't really use that, as it signals one intent, but does something else.
Yes, it’s confusing because of the semantic overlap between push accepting an object reference and _the vector element type’s constructor_ accepting an object reference.
In the vec.push_back(Widget(a, b, c)) case the Widget is constructed first, then it gets pushed to the container. At this point the container checks if it has enough storage and expands its storage if it needs to. Then the Widget is copied/moved into the containers storage. So the ordering would be: construct, check, resize, move/copy.
While in the vec.emplace_back(a, b, c) case the container can check if it has space first before constructing the Widget directly inside the container. So the ordering would be: check, resize, construct.
So you would need some exceptionally special circumstances for this conversion from the push case to the emplace case to occur.
No, emplace_back is supposed to be a contract about non-copyable types.
For trivially copyable types it is useless.
The nuance is when types have complex or expensive rules for copying them. Here you want to be explicit about your intent w.r.t. copying.
I would summarize it as: use `push_back` when you're pushing something, use `emplace_back` when you want to construct something in the space at the end of the collection. It just so happens that their functionality overlaps, and there really isn't a strong reason to use `push_back(Widget(ctor_arg1, ctor_arg2))` vs `emplace_back(ctor_arg1, ctor_arg2)` with a movable `Widget`, so it comes down to readability and communicating intention. And those will depend on the situation.
Mentioning `Widget` is definitely more explicit, so I agree with the author that `push_back(Constructor(...))` is usually better. Semantically, it's also usually simpler to think of "push a newly-constructed Widget" vs "construct a Widget in the next element of the vector and lengthen the vector to include it".
In our pre-C++11 library we had containers with a append_uninitialized_element() and called placement new on that with an explicit ctor. While syntactically phony it had an unambigous semantic. Code review had to check (remember, pre clang-tidy times) that the method way never used alone.
> The student dutifully changed the line, and both tools reported their satisfaction with the replacement
clang-tidy's `modernize-use-emplace` check warns for both the original version that uses `push_back` (as quoted in the article) and for the modified version that uses `emplace_back`:
warning: unnecessary temporary object created while calling emplace_back [modernize-use-emplace]
13 | widgets.emplace_back(Widget(foo, bar, baz));
| ^~~~~~~ ~
It appears that this check was improved to add this warning at some point between clang v14 and now. At least things have improved since the article was written.
What is the Real World impact of a change like this? From the sample code, the person is building some kind of GUI with widgets. How many extra assembly instructions are performed as a result of this particular push_back vs emplace? In my experience with C++ and Qt, the amount of execution time spent building the GUI trivial compared to the paint (or business logic).
The amount of time spent on these trivial issues in C++ never ceases to amaze me about the "C++ crowd". How many more "C++: Back to the basics" talks do we have to sit through? I await the C++ programmers whom will quickly reply to this post: "But, performance!"
That said, this author has appeared many times before on HN. He is an excellent writer.
"A placeholder name for an unnamed, unspecified, or hypothetical manufactured good or product, typically as an example for purposes of explaining concepts."
If someone spent their time learning their tools, they will make better choices when writing the code without any additional time cost.
There are two variants of very similar code. Both do the same thing, both are readable and maintainable. The difference is not primarily in performance, it's in quality of craft.
Sometimes I think rvalue-references were a bad idea. Sure they improve efficiency for people using objects directly instead of through a pointer, but the amount of mental model added worries me that the average coder will have no real clue what rvalue references are. People really overestimate how much most coders understand.
Agree. Let's introduce xvalue, glvalue and prvalue to language spec. Let's define special cases for "auto". Then add some more ( decltype(auto) ).
Let's add && overloading. Since it does not work automatically anyway, add move and forward.
Now let's blame programmer for calling or not calling move or forward in specific place, because obviously when implementing algorithm the implicit lexical lifetimes of temporaries are what every programmer should focus his attention on.
They are quite complex and with a lot of ugly corners. I cannot but think that there must be a better solution.
The problem is that smarter and better programmes than me couldn't find one that would fit cleanly in the existing language after almost a decade of trying (boost had library based move emulation at the turn of the millennium, and the same authors came up with universal refs).
The reason I find myself preferring push_back is that I can use aggregate init with it. Which is usually preferable to the uglier but faster emplace_back in places where it doesn't matter.
For large data frames, I just use std::move if need the object to be owned by the vector or map for indexed access. If you don't want to make a mistake, use noncopyable to make sure.
114 comments
[ 4.2 ms ] story [ 173 ms ] threadOr maybe use whatever the fuck you want and let other to decide for themselves. Often people even do not have a choice.
And you know why they don't have the choice to not use C++? Because someone else made a choice to use C++ and so here we are. That's the paradox of having a freedom in chosing the language: only the first contributor has that freedom, everyone else either has to accept their decision, or leave.
Go is simpler because it is limited in functionality.
List<T> lst = new();
lst.Add(obj);
Done. Sure go is nice if you only care about being hip and trendy, but C# is better in more situations than you'd think. Why bother with new languages and absurd syntax when C# has been around for decades and has perfectly clear syntax that spells out exactly what you want in simple English?
Don't be an asshole. "Just use today's trendy language instead of crusty old C++" makes you an asshole. Stop it.
The discrimination is the penalty of copying a stack-value vs some GC’d/managed memory. Then if you compare those scenarios the semantics lead to more interesting topics for debate.
That’s what C++ vector emplace_back does. It allocates the memory if needed, then constructs the object in place using the provided arguments. No need for a copy.
I'm confused as to how you could initialize dynamic memory without a copy. I feel like initializing dynamic memory _is_ copying (memcpy), unless we're talking about some sort of fully constexpr thing.
1/ allocates the memory for an element in for instance:
2/ calls which directly creates the object and calls my_type::my_type(int) constructor in the std::vector's memory storage. The only output you'll see will be The inner std::string will also be initialized directly in the right memory position, at no point there will be a copy of, say, 10000 'A' characters.In the second case, first you construct my_type on the stack of the calling code so you get a first call to
Then my_type is moved (or copied, if it didn't have move constructors): std::vector's implementation does pretty much the same thing, but the result is two constructions instead of one: which ends up calling my_type::my_type(my_type&&) ; you'll see and the inner string will be copied / moved tooYou know, maybe forwarding the arguments is the wrong direction. Maybe there should have been the dual mechanism of back-propagating the ultimate destination of the object all the way back to its constructor instead.
bar foo() {bar baz; ...; return baz;}
bar qux = foo();
'baz' is constructed directly in the space allocated for 'qux'.
Implementation notes: the complex value is never returned but rather the caller passes the address of a space where the return value should be constructed. Inside the function, the compiler notes that baz is returned and allocates it in the return value space.
This optimization is guaranteed to occur. Compilers which don't do this are nonconforming.
I vaguely remember that there are some obscure corner cases when it's won't occur but I can't recall them since I haven't written C++ since 2016.
NRVO is not guaranteed to occur, only unnamed RVO (`bar foo() { return bar(...) ; }`) is.
Production code in Rust is littered with imperformant `.clone()`s but at least I can see where they're happening to ponder a better way.
You might say "Big deal, it's just a tiny loss of performance to create a value and then copy it into its final place. Unlike C++ this is guaranteed to only do a copy of bytes, no complex code like a copy ctor. Who cares, especially if it's only noticeable in debug builds?" But it's not just a problem of performance. If it's `Box::new([0_u8; 10 * 1024 * 1024])`, then that 10 MiB array created in the caller's stack can end up blowing the caller's stack.
Rust did actually try to add emplace style APIs and a dedicated operator. It would've looked like `vec.place_back() <- Widget::new();` and it would've guaranteed that the generated code did not create a copy in the caller frame. It was never stabilized and instead eventually removed, because it was still not reliable enough to provide that guarantee after all.
https://github.com/rust-lang/rfcs/blob/master/text/1228-plac...
https://doc.rust-lang.org/1.26.0/std/vec/struct.Vec.html#met...
https://github.com/rust-lang/rust/issues/27779#issuecomment-...
Box::<u8>::new_zeroed_slice(10 * 1024 * 1024) says we want 10MiB of zero bytes as a MaybeUninit inside a box, we can then (since these are just bytes in our example) assume_init() since that's valid for our type although in the real world probably we'd actually store some actual data in the memory we've allocated - but it doesn't go on the stack.
If we're overwriting it all anyway there's also an adjacent set of uninit functions to skip the zero step, although of course the OS might be zeroing the page anyway.
What I'm thinking of would be
```c++ struct MyBigClass { /* lots of members */ };
MyBigClass makeABigOne();
auto main() -> int { // We still need to construct a return slot here, even though we don't use the value, right? makeABigOne(); } ```
Perhaps this is tangential, but I'm wondering if maybe I'm missing a subtly based on what you mean by "allocated" (in the abstract machine, or in the ABI).
So yes, storage for the (to-be discarded) object must be allocated, and the object is constructed into that storage.
I don’t know enough about the ABI to comment about the last point.
https://en.cppreference.com/w/cpp/language/copy_elision
This Google tip of the week gives a good explanation (explicit vs implicit) for why to prefer push back vs emplace if you don't need the benefit of emplace: https://abseil.io/tips/112
In particular:
> Let me answer that question by asking another: what do these two lines of code do?
vec1.push_back(1<<20); vec2.emplace_back(1<<20);
> The first line is quite straightforward: it adds the number 1048576 to the end of the vector. The second, however, is not so clear. Without knowing the type of the vector, we don’t know what constructor it’s invoking, so we can’t really say what that line is doing
On deque it all feels fairly natural. Ruby's shift and unshift are cool, but I always struggle to remember the words
ruby and js got shift and unshift from perl, which got shift from sh, where it's the only way to iterate over an array or other list. (also, sh only has one array)
in the old country, instead of shift and unshift, we said cdr and cons. we didn't have push and pop; if we wanted to mess with the backside of a list we had to reverse it first. but our code ran ten times as fast as ruby and had implicit undo
at some point don hopkins suggested, i think it was, eat and barf for, respectively, push and pop on the front end. the back end would then necessarily be boof and shit (not shift). betcha wouldn't forget which end those pertained to
The thought was to have the symmetry of push and pop, on both ends of a sequence container. See table 11 on page 22 (actually page 24 of the PDF).
What I haven't gotten used to is the terms "shift" and "unshift" from other languages. But I never get confused by C++'s push_front/pop_front.
And I think the consistency has some value. Is it enough value to warrant the extra verbosity? Meh, I think it's more or less a wash.
A search for the opposite of "append" suggests "remove", "subtract", "detach", "disconnect", "disjoin", "unfasten", "reduce", "diminish", and more. None of them really have the same "at the back" connotation that "append" has.
"Push" and "pop" are well known from stacks so it makes sense to use them for operations on one end of list-like containers. That still leaves the problem of what to call the similar operations on the other end.
I suppose we could sidestep that problem by only providing them at one end. So say push() adds to the back and pop() removes from the back.
If you want to operate at the front just reverse the list, do your push or pop, and then reverse the list again and let the optimizer deal with eliminating the two reverses. :-)
Either of these are more explicit and clear to me:
... even if computationally worse.> Without knowing the type of the vector, we don’t know what constructor it’s invoking, so we can’t really say what that line is doing.
I am saying 1-arg constructors that aren't semantically "conversions" from that arg need to be be explicit, not implicit. If you make them implicit, that's your mistake; tools can catch that mistake statically.
I don't get the point of your question. You might as well ask: "Let's say someone in a library somewhere has defined their type's operator< to wipe your disk. If you see std::less<>()(a, b), can you say what the call does without knowing the types?"
To which the answer is... well, obviously, no. Same answer to your question.
What is this supposed to imply?
Someone has made an implicit constructor that makes a network call because... people are dumb. This implicit constructor takes an integer.
Does the following code make a network call?
> Without knowing the type of the vector, we don’t know what constructor it’s invoking, so we can’t really say what that line is doing.
[1] https://en.cppreference.com/w/cpp/language/converting_constr...
How is this not caught by the type system? 2<<20 is clearly no way, whatsoever, compatible with a vector<int> type.
With push back it would be caught by the compiler because you can't implicitly convert 2<<20 to a vector<int>
If you are doing professional development in C++ you should learn the difference and how to recognize incorrect usages
I'm not a professional C++ developer, but I'm obviously required to use the language every once in a while and I vastly prefer code that requires me to have to look at fewer implementations of potentially implicit and hidden things.
It happens to be that you can use emplace_back in place of push_back because copies and moves are just constructor overloads in C++. You shouldn't really use that, as it signals one intent, but does something else.
In the vec.push_back(Widget(a, b, c)) case the Widget is constructed first, then it gets pushed to the container. At this point the container checks if it has enough storage and expands its storage if it needs to. Then the Widget is copied/moved into the containers storage. So the ordering would be: construct, check, resize, move/copy.
While in the vec.emplace_back(a, b, c) case the container can check if it has space first before constructing the Widget directly inside the container. So the ordering would be: check, resize, construct.
So you would need some exceptionally special circumstances for this conversion from the push case to the emplace case to occur.
Mentioning `Widget` is definitely more explicit, so I agree with the author that `push_back(Constructor(...))` is usually better. Semantically, it's also usually simpler to think of "push a newly-constructed Widget" vs "construct a Widget in the next element of the vector and lengthen the vector to include it".
clang-tidy's `modernize-use-emplace` check warns for both the original version that uses `push_back` (as quoted in the article) and for the modified version that uses `emplace_back`:
https://godbolt.org/z/sE7jWacTfIt appears that this check was improved to add this warning at some point between clang v14 and now. At least things have improved since the article was written.
The amount of time spent on these trivial issues in C++ never ceases to amaze me about the "C++ crowd". How many more "C++: Back to the basics" talks do we have to sit through? I await the C++ programmers whom will quickly reply to this post: "But, performance!"
That said, this author has appeared many times before on HN. He is an excellent writer.
"A placeholder name for an unnamed, unspecified, or hypothetical manufactured good or product, typically as an example for purposes of explaining concepts."
There are two variants of very similar code. Both do the same thing, both are readable and maintainable. The difference is not primarily in performance, it's in quality of craft.
It's not so much a performance tool as a tool for consistency guarantees.
The problem is that smarter and better programmes than me couldn't find one that would fit cleanly in the existing language after almost a decade of trying (boost had library based move emulation at the turn of the millennium, and the same authors came up with universal refs).
But it might be interesting to note that since C++20, emplace_back can also (kinda-sorta) involve aggregate initialization: https://quuxplusone.github.io/blog/2022/06/03/aggregate-pare...
> With Clang trunk on my laptop, I get consistently about 1.0s for the push version, and 4.2s for the emplace version.
Wow that is why we're not using C++ at work.
Don’t blindly prefer emplace_back to push_back - https://news.ycombinator.com/item?id=26339893 - March 2021 (140 comments)