[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)