[HN Gopher] Don't blindly prefer `emplace_back` to `push_back`
___________________________________________________________________
Don't blindly prefer `emplace_back` to `push_back`
Author : fanf2
Score : 61 points
Date : 2024-07-28 20:42 UTC (2 hours ago)
(HTM) web link (quuxplusone.github.io)
(TXT) w3m dump (quuxplusone.github.io)
| 38 wrote:
| [flagged]
| Seattle3503 wrote:
| I write Rust mostly, but still found the article interesting
| and accessible.
| FpUser wrote:
| >or maybe just dont use C++
|
| Or maybe use whatever the fuck you want and let other to decide
| for themselves. Often people even do not have a choice.
| Joker_vD wrote:
| > 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.
| gumby wrote:
| WTF is "modern syntax"?
| Joker_vD wrote:
| Probably "the types go after the variable name in the
| declaration"? But even that actually pre-dates C.
| mgaunard wrote:
| Clearly you didn't understand what emplace does.
|
| Go is simpler because it is limited in functionality.
| IshKebab wrote:
| If you want simple you can just you push_back everywhere. It's
| what everyone did for decades.
| mystified5016 wrote:
| 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.
| jcelerier wrote:
| 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
| leecommamichael wrote:
| 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.
| pavlov wrote:
| _> "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.
| Joker_vD wrote:
| 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.
| immibis wrote:
| 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.
| rtpg wrote:
| I really appreciate Rust's copy/clone semantics when reading
| this. The "hopefully" is so worrying.
|
| 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.
| wrsh07 wrote:
| > 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
| CamperBob2 wrote:
| 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.
| wrsh07 wrote:
| I wonder if they designed the APIs for vector, list, deque,
| etc together
|
| On deque it all feels fairly natural. Ruby's shift and
| unshift are cool, but I always struggle to remember the words
| beached_whale wrote:
| 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
| CamperBob2 wrote:
| Append implies the back or end of something, while
| "prepend" would be the analogous term for the front.
| the_mitsuhiko wrote:
| 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.
| a1o wrote:
| CLion does the correct replacement on it's right click fix
| actions. Just if anyone using is reading.
| the_mitsuhiko wrote:
| And what is the right fix? Does it recommend std::move with
| push_back or does it use emplace?
| a1o wrote:
| The one from clang-tidy the author mentions.
| the_mitsuhiko wrote:
| 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.
| davidcbc wrote:
| > I would bet most people are unaware of how emplace
| actually works and for a reviewer it's more cognitive
| overhead too.
|
| If you are doing professional development in C++ you
| should learn the difference and how to recognize
| incorrect usages
| the_mitsuhiko wrote:
| 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.
| knorker wrote:
| 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.
| anonnon wrote:
| O'Dwyer's "Back to Basics" talks on YouTube are worth a watch for
| anyone looking to get up to speed on modern C++.
| addicted wrote:
| I'm no C++ expert but couldn't what emplace_back does not be
| achieved through compiler optimizations?
| sfink wrote:
| 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".
___________________________________________________________________
(page generated 2024-07-28 23:02 UTC)