[HN Gopher] I have no constructor, and I must initialize
       ___________________________________________________________________
        
       I have no constructor, and I must initialize
        
       Author : cyber1
       Score  : 264 points
       Date   : 2024-07-05 08:42 UTC (14 hours ago)
        
 (HTM) web link (consteval.ca)
 (TXT) w3m dump (consteval.ca)
        
       | jakewins wrote:
       | Man I get vertigo reading this. Reminds me of trying to
       | understand Java constructors and object initialisation.
       | 
       | It's been a while now, and at least in my experience so far Go
       | and Rusts choice of not having special constructors really
       | simplifies a lot.
       | 
       | Is there anyone that's had the experience of missing constructors
       | once you swapped away from them?
        
         | _ZeD_ wrote:
         | dude, java constructor are _easy_... that C++ stuff is really
         | black magic
         | 
         | and from what I understand rust constructors are basically the
         | same as java, no?
        
           | masklinn wrote:
           | Rust does not have constructors at all[0], it uses factory
           | functions (conventionally named `new_somethignsomething`) but
           | those are not special to the language.
           | 
           | [0] except in the more generalised haskell-ish sense that
           | structs or enum variants can be constructed and some forms
           | ("tuple structs" and "tuple variants") will expose an actual
           | function
        
             | collinvandyck76 wrote:
             | I've often longed for first class constructors in Go and
             | Rust. It was more of a problem for me with Go because you
             | can omit a struct field when building a value, something
             | you can't do in Rust unless it has an explicit Default impl
             | and even then you have to explicitly add
             | ..Default::defualt() when you're building the value.
             | 
             | I never thought that constructors were that burdensome and
             | therefore do not understand the omission in other languages
             | like Go and Rust that followed. Quite the opposite really
             | -- knowing that a type always went through a predefined
             | init was comforting to me when writing Java.
        
           | mmaniac wrote:
           | Rust doesn't have constructors. By convention, a static
           | method called new returns a struct - no magic.
        
           | jakewins wrote:
           | I think if you think constructors in Java are easy, you are
           | much, much smarter than I am or have missed some really,
           | really subtle footguns.
           | 
           | Eg:
           | 
           | - Java constructors can return the object before they
           | complete construction, finishing at a later time; this is
           | visible in concurrent code as partially constructed objects
           | 
           | - Java constructors can throw exceptions _and_ return the
           | partially constructed object at the same time, giving you
           | references to broken invalid objects
           | 
           | - Just.. all the things about how calling super constructors
           | and instance methods interleaved with field initialization
           | works and the bazillion ordering rules around that
           | 
           | - Finalizers in general and finalizers on partially
           | constructed objects specifically
           | 
           | I don't in any way claim it's on the same level as C++, but
           | any time I see a Java constructor doing _any_ method calls
           | anymore - whether to instance methods or to super
           | constructors - I know there are dragons
        
             | SpaghettiCthulu wrote:
             | > - Java constructors can return the object before they
             | complete construction, finishing at a later time; this is
             | visible in concurrent code as partially constructed objects
             | > > - Java constructors can throw exceptions _and_ return
             | the partially constructed object at the same time, giving
             | you references to broken invalid objects
             | 
             | Java constructors do not actually return the object. In
             | Java code, it would appear to the caller as though the
             | contructor returns the new instance, but that is not really
             | the case. Instead, the new object is allocated and then the
             | constructor is called on the object in (almost) the same
             | manner as an instance method.
             | 
             | Additionally, Java constructors can only leak a partially
             | initialized object if they store a `this` reference
             | somewhere on the heap (for example, by spawning a thread
             | with a reference to `this`). The assertion that this gives
             | you a reference to a "broken invalid object" is only
             | potentially correct from the perspective of invariants
             | assumed by user-written code. It is perfectly valid and
             | well-defined to the JVM.
             | 
             | > - Just.. all the things about how calling super
             | constructors and instance methods interleaved with field
             | initialization works and the bazillion ordering rules
             | around that
             | 
             | This is a gross mischaracterization of the complexity.
             | There is only a single rule that really matters, and that
             | is "no references to `this` before a super constructor is
             | called". Until very recently, there was also "no statements
             | before a super constructor is called".
             | 
             | > - Finalizers in general and finalizers on partially
             | constructed objects specifically
             | 
             | Finalizers are deprecated.
        
             | cvoss wrote:
             | > bazillion ordering rules
             | 
             | There are 3 which pertain to object initialization in Java.
             | 
             | 1. super is initialized in it's entirety by an implicit or
             | explicit call to `super()`
             | 
             | 2. All instance initializers of the present class are
             | invoked in textual order.
             | 
             | 3. Constructor code following the `super()` call is
             | executed.
             | 
             | The only awkward thing here is the position of #2 in
             | between #1 and #3, whereas the text of a constructor body
             | suggests that #1 and #3 are consecutive. It gets easier to
             | remember when you recognize that, actually, there's a
             | defect in the design of the Java syntax here. A constructor
             | looks like a normal function whose first action must be a
             | `super()` call. It's not. The `super()` call is it's own
             | thing and shouldn't rightly live in the body of the
             | constructor at all.
             | 
             | Edit: Tweaks for clarity.
        
             | vips7L wrote:
             | I think you're exaggerating the complexity here. There are
             | corner cases yes, but the compiler will warn you about
             | them.
        
             | marcosdumay wrote:
             | Those are the normal issues inherent to constructors as a
             | concept (except for the finalizer one).
             | 
             | Any language that has constructors has some complex rules
             | to solve those things. And it's always good to check what
             | they are when learning the language. Java has one of the
             | simplest set of those rules that I know about.
        
           | DougBTX wrote:
           | Inside a constructor you can access a partially initialised
           | "this" value, and even call methods on it, which leads to
           | rules like: "Do not call overridable methods in
           | constructors"[0], as they can lead to surprising, non-local,
           | bugs.
           | 
           | Rust has functions associated with types which are
           | conventionally used like constructors, but critically the new
           | objects must have all their fields provided all at once, so
           | it is impossible to observe a partially initialised object.
           | 
           | [0] https://learn.microsoft.com/en-
           | us/dotnet/fundamentals/code-a...
        
             | titzer wrote:
             | Virgil solved this a little differently. The initialization
             | expressions for fields (outside of constructors) as well as
             | implicit assignment of constructor parameters to fields
             | happens before super constructor calls. Such initialization
             | expressions cannot reference "this"--"this" is only
             | available in _constructor bodies_. Initializing fields
             | before calling super and then the chaining of super calls
             | guarantees the whole chain of super constructor calls will
             | finish before entering the body of a constructor, and all
             | fields will be initialized. Thus by construction, virtual
             | methods invoked on "this" won't see uninitialized fields.
             | 
             | https://github.com/titzer/virgil/blob/master/doc/tutorial/C
             | l...
        
             | zozbot234 wrote:
             | You can most likely use session types to soundly observe a
             | partially initialized MaybeUninit<MyObject> in Rust. The
             | proper use of session types could ensure that the object is
             | only assumed to be initialized after every field of it has
             | been written to, and that no uninitialized fields are ever
             | accessed in an unsound way. The issue though is that this
             | is not automated in any way, it requires you to write
             | custom code for each case of partial initialization you
             | might be dealing with.
        
         | masklinn wrote:
         | There are a few somewhat esoteric cases where constructors
         | working in-place allow magic which can be hard to replicate
         | otherwise e.g. Rust is still missing _guaranteed_ "placement
         | new" type behaviour.
         | 
         | Unless you want to `ptr::write` individual fields by hand into
         | a `MaybeUninit`, which you can absolutely do mind but that...
         | is not very ergonomic, and requires structs to be specifically
         | opted into this.
        
           | wongarsu wrote:
           | Which can be an issue if you want to initialize a 2MB large
           | heap-allocated object (e.g. heap-allocating a large nested
           | struct or a big array).
           | 
           | Without guaranteed "placement new" that can mean that your
           | 2MB object gets constructed on the stack and copied to the
           | heap. And while Linux defaults to a 4MB stack, Windows
           | defaults to 1MB and will crash your program. Or it might work
           | if the compiler optimizes in your favor.
           | 
           | It's not something you encounter frequently, it can be worked
           | around, and Rust will eventually solve it ergonomically
           | without introducing constructor hell (probably with just a
           | keyword). But finding the best language-level solution isn't
           | straightforward (efforts to fix this for rust are ongoing for
           | 9 years)
        
             | bennettnate5 wrote:
             | It can also be an issue if you want to wrap any API that
             | requires fixed memory locations for objects (such as POSIX
             | semaphores). It's UB to call a POSIX semaphore from any
             | other memory location than where it was initialized, so
             | making a `Semaphore::new()` API is just asking for trouble.
             | You can deal with it by `Box`ing the semaphore, but then
             | you can't construct the semaphore in a shared memory
             | segment (one of the stronger use cases for process-shared
             | semaphores).
             | 
             | I have a hunch this is why there's no Semaphore
             | implementation in the Rust standard library, though it
             | could be due to fundamental inconsistencies in semaphore
             | APIs across OSs as well -\\_(tsu)_/-
        
               | masklinn wrote:
               | No, Rust doesn't have semaphores in the stdlib[0] because
               | it was not clear what precise semantics should be
               | supported, or what purpose they would serve since by
               | definition they can't mitigate exclusive and thus write
               | access to a resource and mitigating access to _code_ isn
               | 't much of a rust convention. And nobody has really
               | championed their addition since.
               | 
               | Furthermore, they still present a fair amount of design
               | challenges in the specific context of Rust:
               | https://neosmart.net/blog/implementing-truly-safe-
               | semaphores...
               | 
               | [0] technically they were there, added in 0.4, never
               | stabilised, deprecated in 1.7, and removed in 1.8
        
             | godshatter wrote:
             | >Which can be an issue if you want to initialize a 2MB
             | large heap-allocated object (e.g. heap-allocating a large
             | nested struct or a big array).
             | 
             | >Without guaranteed "placement new" that can mean that your
             | 2MB object gets constructed on the stack and copied to the
             | heap. And while Linux defaults to a 4MB stack, Windows
             | defaults to 1MB and will crash your program. Or it might
             | work if the compiler optimizes in your favor.
             | 
             | C gets a lot of hate, often for good reasons, but at least
             | you know where your memory is coming from when you are
             | allocating it yourself. If you're allocating a large heap-
             | allocated object, you're grabbing the memory directly from
             | the heap.
        
               | wongarsu wrote:
               | Memory allocation is one of the areas where currently
               | C/C++ has or had genuine advantages over Rust. Custom
               | allocators took Rust years, and giving standard library
               | constructs like a Vector a custom allocator that isn't
               | the global allocator is still experimental (=opt-in
               | nightly-only). Similarly while Rust gives you good
               | control over where the data ends up being stored, there
               | is no way to make sure it isn't also put on the stack
               | during function execution. One of the implicit
               | assumptions underlying the language seems to be that the
               | stack is cheap and effectively infinite while the heap is
               | expensive. So you have a lot of control over what touches
               | the heap, but less control over what touches the stack.
               | 
               | Those are temporary pains that have remedies in the
               | works. Rust is a fairly young language, and a lot of
               | good-enough solutions get thrown out before ever getting
               | beyond the experimental stage. But if you are writing
               | software today then needing absolute control over where
               | exactly your data touches is a good reason to prefer
               | C/C++ today. Not that that's a very common need.
        
       | marton78 wrote:
       | After almost 20 years of experience with C++, there are still
       | some gnarly details I wouldn't have imagined. What a God awful
       | language!
       | 
       | Kudos to that author for the great, eye catching title and the in
       | depth detail!
        
         | unwind wrote:
         | In case it's not known to everyone, the title is an obvious nod
         | to "I Have No Mouth, and I Must Scream" [1], a 1960s US sci-fi
         | story by Harlan Ellison.
         | 
         | 1:
         | https://en.wikipedia.org/wiki/I_Have_No_Mouth,_and_I_Must_Sc...
        
           | bookofjoe wrote:
           | https://talesofmytery.blogspot.com/2018/10/harlan-
           | ellison-i-...
        
           | _vaporwave_ wrote:
           | That plot summary is... dark. Does anyone know how long the
           | story is? Most of the copies I found online are collections
           | of short stories.
        
             | neckro23 wrote:
             | It's a short story, a brief one (~32 kB): https://gist.gith
             | ub.com/neckro/0f3a9ec60be34e3164c6677d4ecc1...
             | 
             | CW though, it _is_ pretty grim. Very early example of the
             | "AI takes over the world, decides humans are redundant"
             | trope though. (Maybe the first?)
        
           | tannhaeuser wrote:
           | The question is which IHNMAIMS character the poster
           | identifies with to have deserved his OOP misery, given all
           | protagonists are imprisoned for life (or for eternity,
           | actually, I believe) as a sentence for the bad things they
           | did ;) Note there's also the adventure game created after the
           | book, overseen and with a script also by Ellison.
        
           | tpoacher wrote:
           | pc adventure game was good too (i.e., messed up)
        
         | wavemode wrote:
         | Always obligatory https://mikelui.io/img/c++_init_forest.gif
        
           | CoastalCoder wrote:
           | Thanks, I'd never seen that one!
           | 
           | So horrifyingly true.
        
             | fouronnes3 wrote:
             | If you enjoy this, a few years ago I made the "C++ iceberg"
             | meme (with clickable links!). I've been thinking about
             | making an updated V2 with all the "you forgot about X"
             | messages I've been getting.
             | 
             | https://fouronnes.github.io/cppiceberg/
        
               | throwup238 wrote:
               | God bless you for making this. I plan to incorporate
               | several of these features at work in the hope of
               | summoning Cthulu and killing the company once and for
               | all.
               | 
               | What's your favorite "you forgot X"? You should
               | definitely make an updated v2 because every link I've
               | opened from the bottom half has been absolutely bonkers.
               | 
               | Three dimensional analog literals drawn using ASCII? What
               | the flying hell was the standards committee thinking.
        
               | java-man wrote:
               | else while
               | 
               | is perfectly fine, not only in c++.
        
               | CoastalCoder wrote:
               | > If you enjoy this
               | 
               | Well, I do appreciate your work, and the information is
               | certainly helpful.
               | 
               | But it's a bit like a urologist explaining what it will
               | be like when you pass a kidney stone.
               | 
               | And then find out that the C++ standards committee is
               | working on a new kidney-stone shape that's backwards
               | compatible, but adds more jagged spikes.
        
         | queuebert wrote:
         | I should print this and put it on my wall for all those times
         | when I'm frustrated with Rust lifetimes.
        
         | GuB-42 wrote:
         | C++ is a popular multi-paradigm language that is both cutting
         | edge and 40 years old (more if you count C), there is simply no
         | way around that level of complexity.
         | 
         | You have "C with classes" that coexist with the "modern" way,
         | full of smart pointers and functional programming. It is
         | popular in embedded systems, video games, servers, and GUIs
         | (mostly Qt). And if you look at the code, it is as if it was a
         | different language, because the requirements are all very
         | different. Embedded system need low level hardware access,
         | video games are all about performance, servers want safety, and
         | GUIs want flexibility.
         | 
         | There are less awful alternative to C++. For example C on one
         | end of the spectrum and Rust on the other end. But none of them
         | cover every C++ use case.
        
           | hu3 wrote:
           | Zig looks promising too.
        
           | marcosdumay wrote:
           | > multi-paradigm
           | 
           | Well, it does unstructured imperative, structured imperative,
           | and OOP imperative!
           | 
           | Except if you count template programming, because that one is
           | pure functional, but only runs at compile time.
        
           | bee_rider wrote:
           | C++ needs a different name from multi-paradigm. Java is a
           | multi-paradigm language. C++ is an omni-paradigm language. If
           | there's a paradigm,
           | 
           | - There's at least an ugly library to do it in C++
           | 
           | - There might be support baked directly into the language
           | 
           | - Or you could do it in Lisp, but that would be too easy
        
             | codeflo wrote:
             | And if you dare to combine two of the paradigms it
             | supports, you get UB.
        
               | snappythrowaway wrote:
               | What is UB?
        
               | MaxBarraclough wrote:
               | _Undefined behaviour_.
               | 
               | Here's an article on the topic: https://cryptoservices.gi
               | thub.io/fde/2018/11/30/undefined-be...
        
           | aaroninsf wrote:
           | > But none of them cover every C++ use case.
           | 
           | Literal lol... this is not an argument in favor of C++.
        
           | slashdave wrote:
           | I would sort of agree, except when c++ was invented, it was
           | even more awful in practice (does anyone remember the chaos
           | around STL and template caches?). So, age isn't really a
           | factor.
        
           | wredue wrote:
           | If you're looking for a language with less complexity than
           | C++, you're surely not going to find that in rust.
        
             | kelnos wrote:
             | I disagree. To me, the complexity described in this article
             | is more complex than anything you'll find in Rust.
             | 
             | Actually, strike that: I'm not sure if it's true or not
             | (though I suspect it is), but it doesn't actually matter.
             | What I'm really getting at here is that there is nothing in
             | Rust that behaves so confusingly or ambiguously as what's
             | described in this article. If you're writing Rust, you'll
             | never have to remember these sorts of rules and how they
             | are going to be applied to your code.
             | 
             | I do agree that _reading_ someone else 's Rust can be a
             | challenge, if they're using Rust's type system to its
             | fullest, and you're (quite reasonably and understandably)
             | not up to speed on the entirety of it. And that _is_ a
             | problem, agreed; though, at least, fortunately it 's not a
             | problem of ambiguity, but more of a learning-curve issue.
             | 
             | But I have never been writing Rust and have been confused
             | about what the code I'm writing might do, never had to
             | consult and puzzle out some obscure documentation in order
             | to ensure that the code I was writing was going to do what
             | I expected it to do. C++ falls so incredibly flat in this
             | department, and that's why I avoid using it like the
             | plague.
        
         | ghosty141 wrote:
         | I've been working with C++ at my job for 2.5 years now and I've
         | already come to this conclusion. Wouldn't wanna use it if there
         | is any other way.
         | 
         | The fact that you can do almost anything IS pretty cool, but
         | without having at least one C++ wizard at hand it can drive you
         | nuts.
        
           | ryandrake wrote:
           | Just another person's opinion: I've been using C++ for my
           | entire career, and to be honest, if I'm starting a new solo
           | project, I reach for it unless there is some major technical
           | reason not to. Yes, it can be messy. Yes, there are footguns.
           | But as a developer, you have the power to keep it clean and
           | not shoot the footguns, so I'm still ok with the language.
           | 
           | If I was starting a new work project with a lot of junior
           | team members, or if I was doing a web project, or a very
           | simple script, fine I'll use a different language. There can
           | definitely be good reasons not to use C++. But I'm at the
           | point in my expertise that I will default to C++ otherwise.
           | I'm most productive where I am most familiar.
        
             | kmoser wrote:
             | > Yes, there are footguns. But as a developer, you have the
             | power to keep it clean and not shoot the footguns, so I'm
             | still ok with the language.
             | 
             | With all due respect to your expertise, the whole idea of a
             | footgun is that it tends to go off accidentally. The more
             | footguns a language contains, the more likely you are of
             | accidentally firing one.
        
               | jeffbee wrote:
               | I think what he's saying is that C++ users don't need to
               | go to the Footgun Outlet Mall and wantonly brandish each
               | of their credit cards. You can leave the subtle parts of
               | the language on the shelf, in many cases.
        
             | 725686 wrote:
             | "you have the power to keep it clean and not shoot the
             | footguns". Really? Do you think footguns are intentionally
             | shot?
        
               | bee_rider wrote:
               | What even is a footgun supposed to be? The analogy
               | doesn't really make sense, in that... I mean the first
               | thing anybody learns about guns is that they are "always
               | loaded" (even when you know they aren't) and you only
               | point them at things you want shot.
               | 
               | Is a footgun a gun that only aims at feet? Because that
               | seems like a dumb thing to invent in the first place. Or
               | is it a gun that happens a to be aiming at feet? That
               | seems like something that could only exist by user error.
               | 
               | I think "enough rope to hang yourself" is a more accurate
               | description of almost every programming languages, since
               | rope is at least intended to be useful (although it is a
               | bit more morbid of an analogy).
        
               | rmwaite wrote:
               | Imagine that you had a gun and one of the features of the
               | gun was that if you had sunglasses on and something in
               | your left pocket, holstering the gun would cause it to
               | immediately fire. You could argue that the gun shouldn't
               | behave this way, but it's also possible that others are
               | dependent on this behavior and you can't remove it.
               | 
               | This is a footgun - the way to avoid the holster firing
               | is to simply not wear sunglasses, or keep something in
               | your left pocket, and then it would never fire. But the
               | problem is that both of those things are extremely common
               | (for good reason). It's a poorly thought out feature
               | because it has severe consequences (potentially shooting
               | your foot) for extremely common situations (wearing
               | sunglasses and using your left pocket).
        
               | bee_rider wrote:
               | I basically don't agree that anybody could depend on this
               | holstering-causes-it-to-fire behavior. Or at least, their
               | use-case requires design compromises that are so
               | unthinkably ridiculous as to make the gun they want
               | something that no reasonable person without that use-case
               | would use.
               | 
               | It is possible that the entire field of programming is
               | full of ridiculous people. But it seems more likely that
               | C++ is like a gun with no safety, or something along
               | those lines.
        
               | AnthonyMouse wrote:
               | A lot of the footguns come from compiler authors wanting
               | to make things UB because it allows them to perform
               | certain optimizations, but then you end up with a lot of
               | things that are formally UB even though in practice they
               | _usually_ do the intuitively expected thing. But then,
               | because the widely done thing is actually UB, the
               | compiler is allowed to do something counterintuitive
               | which causes your program to blow up.
               | 
               | An obvious example is omitting NULL pointer checks.
               | Passing a NULL pointer to certain system library
               | functions is UB even if it would ordinarily be expected
               | to be reasonable, e.g. memset(NULL, 0, 0), so some
               | compilers will see that you passed a pointer to memset,
               | and passing a NULL pointer to memset is UB, therefore it
               | can omit a subsequent NULL pointer check guarding a call
               | to _something else_ when the something else _isn 't_
               | going to behave reasonably given a NULL pointer.
               | 
               | This is an insane footgun, but it also allows the
               | compiler to omit a runtime NULL pointer check, which
               | makes the program faster, so people who care most about
               | performance lobby to keep it.
        
               | samatman wrote:
               | Just to carry onwards with the use/mention distinction
               | we're aggressively erasing here, you seem to question
               | whether actual, real-life footguns exist.
               | 
               | They do! Here's a reference.
               | https://en.wikipedia.org/wiki/Slamfire
               | 
               | Another common way to shoot yourself in the foot is a gun
               | which will go off if you drop it. An example of a gun
               | where early models were especially susceptible is the
               | Lanchester:
               | https://en.wikipedia.org/wiki/Lanchester_submachine_gun
        
               | kelnos wrote:
               | > _I basically don't agree that anybody could depend on
               | this holstering-causes-it-to-fire behavior._
               | 
               | It's an idiom. It's not supposed to be entirely logically
               | consistent. It means what it means because people have
               | decided that it means what it means. Your objections
               | aren't really relevant.
               | 
               | "Footgun" is I think a fairly recent addition to the
               | English lexicon, but it's based on the centuries-old "to
               | shoot yourself in the foot". It seems silly to argue with
               | centuries of English idiomatic usage; no one, by
               | definition, is going to win an argument against that.
        
               | wvenable wrote:
               | Yeah I don't think that's a good analogy. Instead, you
               | have guns that don't let you point at your feet. So you
               | can never shoot yourself there. However, if you ever need
               | to shoot straight down for a legitimate reason, you're
               | out of luck. In C++, you can shoot everywhere without
               | restrictions and sometimes that means shooting yourself
               | in the foot or the head.
        
               | sqeaky wrote:
               | At this point a footgun can stand alone in this industry
               | as a term with its own meaning outside of analogy.
               | 
               | It is any trap in something technical that is likely to
               | cause problems from perceived normal use.
               | 
               | Compare to related terms: "Pit of failure", "Turing
               | tarpit", and "Pit of success".
        
               | samatman wrote:
               | I've long been partial to this formulation:
               | 
               | > _1972 - Dennis Ritchie invents a powerful gun that
               | shoots both forward and backward simultaneously. Not
               | satisfied with the number of deaths and permanent
               | maimings from that invention he invents C and Unix._
               | 
               | Some of us learn to lean to the side right before pulling
               | the trigger...
               | 
               | http://james-iry.blogspot.com/2009/05/brief-incomplete-
               | and-m...
        
               | kelnos wrote:
               | "Footgun" comes from the English idiom "to shoot yourself
               | in the foot", which means "to act against your own
               | interests" (usually accidentally). (Consider similar
               | idioms, like "to dig your own grave".)
               | 
               | I think you're being a bit too literal. It's not an
               | analogy at all, and this has nothing to do with firearms
               | best practices. If we were to define a footgun as "a gun
               | that is only capable of shooting you in the foot" (or
               | perhaps more broadly usefully, "a gun that in theory can
               | be useful, but it is nearly impossibly difficult to make
               | it do anything other than shoot you in the foot"), then
               | the entire point of using the term is to describe
               | something that has no useful, logical purpose, and is
               | unsafe to use, even as designed.
               | 
               | Being "given enough rope to hang yourself" is indeed
               | another good idiom to use for things like this, but the
               | implication is different, I think: when you're given
               | enough rope to hang yourself, the outcome is still very
               | much in your hands. You can intentionally or
               | unintentionally use that rope to hang yourself, or you
               | can be reasonably expected to use that rope in another
               | way that would turn out to be safe or useful.
               | 
               | "Footgun", by contrast, is used to describe something
               | that has no (or negligible) safe uses. Maybe the original
               | intent behind the design of what's being described that
               | way was to have safe uses, but ultimately those safe uses
               | never really panned out, or were so dwarfed by the unsafe
               | uses that the safe use isn't worth the thing existing in
               | the first place. But, unfortunately, there are some
               | people -- maybe only 0.01% of people -- who are able use
               | it safely, and critically depend on that safe use, so we
               | can't completely destroy all these footguns and save
               | everyone else from the error of their ways. And
               | unfortunately most everyone else sees these 0.01% of
               | uses, and believes they are so useful, so efficient, so
               | brilliant, they want to try it too... but in their hubris
               | they end up shooting themselves in the foot, like most
               | others before them.
        
               | sqeaky wrote:
               | There are more an less risky behaviors. This is really
               | well explored space in C++. Just using value semantics,
               | shared_ptr, and RAII instead of naked news and reckless
               | mallocs would improve several "old" codebase I have
               | worked in. Maybe people shouldn't be reaching for
               | const_cast so often, and similar. In some new languages
               | some corner case may be unexplored.
               | 
               | If you are fortunate enough for your domain to have good
               | IO libraries then there is a chance you can do everything
               | the "Modern" way avoid a lot of the headache and avoid
               | most of the footguns entirely. That maturity and omni-
               | pattern availability is potent, but all that power does
               | come with the possibility of mistakes or surprises.
               | 
               | Compare to newer languages where we don't know what might
               | break or might need to do something the language omits as
               | part of its paradigm. I have done a ton of Ruby projects
               | and about half the time we need more performance so I
               | need to bust out C to rewrite a hotspot in a performance
               | sensitive way. Or sometimes you just really want a loop
               | not a functional stream enumerator like is the default in
               | Ruby. For a new language, Theo tried the 1 billion row
               | challenge in Gleam and the underlying file IO was so slow
               | the language implementers had to step in.
               | 
               | This is an engineering and a business choice. There are
               | reasons to avoid C++ and footguns, like any risk, are
               | among them. These aren't risks without mitigation, but
               | that mitigation has a cost. Just like newer languages
               | have reasons not to use them. A team needs to pick the
               | tools with risks and other cons they can handle and the
               | pros that help them solve their problem.
        
               | kelnos wrote:
               | > _There are more an less risky behaviors._
               | 
               | The problem is that your definition of risk may not be
               | the same as others', and so there isn't always agreement
               | on what is ok and not ok to do. And regardless, humans
               | are notoriously bad at risk assessment.
               | 
               | > _This is really well explored space in C++. Just using
               | value semantics, shared_ptr, and RAII instead of naked
               | news and reckless mallocs would improve several "old"
               | codebase I have worked in. Maybe people shouldn't be
               | reaching for const_cast so often, and similar._
               | 
               | Right, and all that is exactly the point: all of that
               | stuff is in wide use out there, and I suspect not just in
               | "old" code bases. So there's still not consensus on
               | what's safe to use and what's too risky.
               | 
               | And regardless, I have enough to think about when I'm
               | building something. Remembering the rules of what
               | language features and APIs I should and shouldn't use is
               | added noise that I don't want. Having to make quick risk
               | assessments about particular constructs is not something
               | I want to be doing. I'd rather just write in a safer
               | language, and the compiler will error out if I do
               | something that would otherwise be too risky. And as a
               | bonus, other people are making those risk assessments up-
               | front for me, people in a much better position than I am
               | to do so in the first place, people who understand the
               | consequences and trade offs better than I do.
               | 
               | I _really_ like this value proposition:  "if the compiler
               | successfully compiles the code, there will be no buffer
               | overruns or use-after-free bugs in it" (certainly there's
               | the possibility of compiler bugs, but that's the only
               | vector for failures here). For C++, at best, we can only
               | say, "if you use only a particular subset of the language
               | and standard library that the compiler will not define or
               | enforce for you (find a third party definition or define
               | it yourself, and then be very _very_ careful when coding
               | that you adhere to it, without anyone or anything
               | checking your work), then you _probably_ won 't have any
               | buffer overruns or use-after-free bugs." To me, that's
               | almost worse than useless; even if I find a C++-subset
               | definition that I think is reasonable, I'm still not
               | really protected, because I still have to perfectly
               | adhere to it. And even if I do, I'm still at the mercy of
               | that subset definition being "correct".
        
             | alex_lav wrote:
             | > I've been using C++ for my entire career, and to be
             | honest, if I'm starting a new solo project, I reach for it
             | 
             | This is "I use the language I always use because I always
             | use it", and not actually a praise or C++ specifically.
        
               | sqeaky wrote:
               | Presumably "entire career" means some amount of exposure
               | to other things.
               | 
               | In my 21 years coding professionally, I will settle on
               | C++ or Ruby for most problems depending on context. Ruby
               | for quick, dirty, and "now!", while I use C++ for Long
               | lasting, performance, strongly typed, and compatible
               | things. Those aren't perfect categories and there are
               | reasons to pick other tools, but Choosing C++ after a
               | long career does mean something more than "I am
               | comfortable with this".
        
           | philsnow wrote:
           | I don't think I've ever gotten paid for a line of c++ but
           | Google has a "style guide" for internal c++ code that omits
           | something like 3/4 of the language, and people seemed pretty
           | happy with it overall. Maybe not "happy" but "grudgingly
           | accepting because it beats the Wild West alternative".
        
             | fsckboy wrote:
             | is google's "internal" style guide this?
             | 
             | https://google.github.io/styleguide/cppguide.html
        
               | JasonSage wrote:
               | Nit: parent didn't call it an internal style guide, but a
               | style guide for their internal C++.
               | 
               | (I'm sure it is.)
        
               | fsckboy wrote:
               | Nit nit: I don't accept your quibble, I think my usage
               | was well within English usage standards; I even put
               | "internal" in quotes! Consider this hypothetical
               | conversation:
               | 
               | "Is the style guide they use for internal projects the
               | same as this style guide that they have published
               | externally?"
               | 
               | "could you clarify which ones you're talking about?"
               | 
               | "Is the internal style guide you described the same as
               | this one I found in google's account on github?"
               | 
               | "oh, I see what you mean"
               | 
               | will you send your second, or shall we simply pistols-at-
               | dawn?
        
               | Maxatar wrote:
               | >"Is the style guide they use for internal projects the
               | same as this style guide that they have published
               | externally?"
               | 
               | Consider that the style guide that AirBnB uses for
               | internal projects is not the same as the style guide they
               | publish externally, and you can sympathize with why the
               | distinction matters :P.
        
               | samatman wrote:
               | Nit^3: this point would have been effectively conveyed as
               | '"Google's internal style guide"'. By putting only
               | "internal" into quotes, you call into question whether
               | its public existence invalidates the internal nature of
               | it.
               | 
               | Whereas the respondent said this:
               | 
               | > _Google has a "style guide" for internal c++ code_
               | 
               | This is a style guide for definitely-internal c++ code,
               | with the internality of the style guide itself
               | unspecified. I'm not sure what the effect of the scare
               | quotes around "style guide" is meant to be, just that it
               | doesn't move the internal part next to the style guide
               | part.
               | 
               | Putting the whole thing in quotes, rather than just
               | "internal", questions whether the guide you found is the
               | guide referred to, rather than the internal nature of the
               | style guide itself, which the quoted sentence takes no
               | position on.
               | 
               | This has been your daily dose of Hacker News style guides
               | for discussing style guides.
        
               | kelnos wrote:
               | I think this is a tough one, and different people are
               | going to interpret it differently.
               | 
               | The fact that you put "internal" in quotes suggested to
               | me a mild level of sarcasm or disbelief, i.e, I read your
               | message as "You mean _this_ style guide, published on the
               | internet, for all to see? Clearly that 's not
               | 'internal'!"
               | 
               | Either way, to me, "internal style guide" (regardless of
               | any quotes placed around any word) means "style guide
               | that is internal" (that is, the style guide itself is
               | private or unpublished).
               | 
               | But the person you were replying to called it a "style
               | guide for internal c++ code": that word ordering makes it
               | clear that "internal" is describing "c++ projects", and
               | that the internal/external (or unpublished/published or
               | private/public) status of the style guide itself is not
               | being talked about at all.
               | 
               | (As an aside, if the commenter upthread had instead said
               | "internal style guide for c++ code", that could have also
               | meant the same thing, but would have been ambiguous, as
               | it wouldn't have been clear if "internal" was describing
               | "style guide" or "c++ code", or both, even. But "style
               | guide for internal c++ code" is about as unambiguous as
               | you can get.)
        
             | marcosdumay wrote:
             | C with classes and text-template generics would be an ok
             | subset of the language, if external concepts didn't keep
             | creeping into its semantics. The problem is that they do.
             | 
             | Almost every part of C++ creeps into almost every other
             | part, and C was already complex enough... and let's just
             | ignore that C++ is not completely compatible with C.
        
             | jimbobthrowawy wrote:
             | Is it really 3/4ths the language? (mostly culled libraries,
             | or features?) I remember reading an old pdf published by
             | the US air force about the subset of c++ features you're
             | allowed to use for contracted software, and it's so
             | different it may as well be a different language.
             | 
             | I think I found it via a stackexchange answer about how the
             | "Wiring" language for Arduino sketches differs from regular
             | c++. In Wiring, it's mostly things like no rtti, no virtual
             | methods not resolvable at compile time, no exceptions,
             | unsafe-math, permissive casting, pre-build system
             | concatenates all .ino files into one .cpp file, very
             | limited libraries, and some default includes.
        
         | FreezerburnV wrote:
         | "There are only two kinds of languages: the ones people
         | complain about and the ones nobody uses." - Bjarne Stroustrup
         | 
         | Not disagreeing that C++ is awful in a lot of ways and super
         | difficult though. But I still weirdly like it, personally. I
         | find it a fun challenge/puzzle to work with.
        
           | zarathustreal wrote:
           | I find it annoying to have to solve a puzzle to make progress
           | solving my intended puzzle (i.e. whatever I'm computing)
        
           | umanwizard wrote:
           | I think we can say Rust is beyond the "nobody uses" stage by
           | now, and it's much simpler and easier than C++. (And people
           | who use it tend to like it, proving Bjarne wrong).
        
             | fiddlerwoaroof wrote:
             | Or, because there's so many languages around now, they just
             | use something else. I really don't like working with Rust
             | myself and so I use other languages.
        
             | saurik wrote:
             | I'm sorry; you think people don't complain about Rust?
             | There are tons of articles posted here from people
             | complaining about Rust in various ways. Bjarne wasn't
             | saying whether most people like it... that's orthogonal: I
             | actually like C++, yet I have been complaining about it--at
             | times quite _bitterly_ --since before it was even
             | standardized!
        
               | tomjakubowski wrote:
               | Indeed, I am a huge proponent of Rust and have been using
               | it since before 1.0 (even contributed to it, in the past)
               | -- and I complain about Rust a lot, too. Trying to
               | restate Bjarne's point here: if I wasn't using Rust, then
               | I wouldn't have any reason to complain about it.
        
             | alex_lav wrote:
             | Saying people complain about something is not the same as
             | saying nobody likes it...
        
             | dgfitz wrote:
             | Rust is neither simple nor easy. Full stop.
        
           | diffxx wrote:
           | I truly loathe that quote. It is a tautology that is used to
           | deflect legitimate criticism.
        
             | RogerL wrote:
             | And it is not true (for any reasonable reading of the
             | quote). There are very popular languages that don't get the
             | deserved hate that C++ does. Sure, Python is slow,
             | packaging/versioning is painful, but it is nothing like C++
             | complaints.
             | 
             | I mean, a standard (and stupid IMO) interview question is
             | rate your C++ expertise from 1-10, and if you answer more
             | than about 5-6 you get bounced for lying or not recognizing
             | your limitations, while they gleefully point out Stroustrup
             | wouldn't answer 9-10.
        
               | wredue wrote:
               | I mean. Python:
               | 
               | Bolted on, very terrible OO that should never be touched
               | 
               | Some of the most batshit insane ideas of encapsulation
               | anyone has ever seen
               | 
               | Some of the most batshit insane return rules anyone has
               | ever seen
               | 
               | Encouraged inconsistency from the language in the form of
               | functions like "Len" that are added because sometimes
               | someone feels it reads better?
               | 
               | Encouraged unwinding exceptions as _regular flow control_
               | (lol. Yikes)
               | 
               | It is nearly universally agreed that Python has
               | significant code management issues as code bases scale
               | 
               | This is all ignoring debates of fundamental typing
               | issues. Personally, I hate what Python does, but some
               | people seem to prefer it.
               | 
               | Let us not pretend Python doesn't have some language
               | problems on top of its tooling problems.
        
               | gpderetta wrote:
               | You forgot "it is so slow you might be faster with pen
               | and paper".
        
           | catlifeonmars wrote:
           | I feel that if the language is a challenge to work with, it
           | better give you your money's worth. In 2024, there are plenty
           | of other languages with better ROI, if you want a challenge.
           | 
           | In any case, I think the primary goal of any programming
           | language is to get out of your way and let you tackle more
           | interesting problems related to the problem domain that led
           | you to start writing a program in the first place.
        
         | qsdf38100 wrote:
         | So you hate C++. Great, thanks for your informative insights.
        
       | echelon wrote:
       | A language shouldn't be this complicated. This is dangerous and
       | impossible for teams full of juniors and busy people with
       | deadlines. We're only human.
        
         | 082349872349872 wrote:
         | I believe those teams just use constructors; this is a corner
         | case, not SOP.
        
           | echelon wrote:
           | C++ should start pulling things out of the language with new
           | editions. It would improve quality of life dramatically.
        
             | falcor84 wrote:
             | How about (C++)-- ?
        
               | Joel_Mckay wrote:
               | There was a C--, and it was an Assembly macro C like
               | syntax based compiler.
               | 
               | https://en.wikipedia.org/wiki/C--
               | 
               | The GNU gcc/g++ was far more important to standardization
               | than most people like to admit.
               | 
               | Have a great day, =)
        
             | pjmlp wrote:
             | Rust style editions don't work with binary libraries,
             | across compilers, or template code across editions, with
             | semantic differences.
             | 
             | That is why the epochs proposal was rejected.
             | 
             | Additionally, the main reason many of us, even C++
             | passionate users, reach out to C++ instead of something
             | else, is backwards compatibility, and existing ecosystem.
             | 
             | When that is not required for the project at hand, we
             | happily reach out to C#, D, Java, Go, Rust, Zig, Swift,
             | Odin,.... instead.
        
               | jamincan wrote:
               | If Rust style editions can work across modules, why
               | couldn't they work across binary libraries and so forth?
               | The whole point is to allow the language to progress
               | while maintaining backward compatability.
        
               | dathinab wrote:
               | they don't work with a backward compatible application
               | binary interface
               | 
               | or more specifically they only work with ABI stability if
               | they ABI doesn't change between epochs
               | 
               | which isn't a issue for Rust because:
               | 
               | - it is cleanly modularized
               | 
               | - it build a whole module "at once"
               | 
               | - it doesn't have a "stable" ABI (outside of "extern/repr
               | C" parts which don't contain non reprC parts rust doesn't
               | even guarantee ABI compatibility between two builds in
               | exactly the same context*(1))
               | 
               | - tends to build everything from source (with caching)
               | 
               | - a lot of internees are intentionally kept "unstable" so
               | that they can change at any time
               | 
               | on the other side due to how C/C++ build things, doesn't
               | have clean module isolation, how it chooses build units,
               | how all of that is combined, how it's normal to include
               | binaries not build by your project (or even you), how
               | such binaries contain metadata (or don't) and how too
               | much tooling relies on this in ways which make changes
               | hard, how it doesn't have build-in package management,
               | how it you specify compiler options and how compiler
               | defaults are handled etc. come together to make that
               | impossible
               | 
               | in a certain way how you specify that you use C++11,17
               | etc. is the closest C++ can get to rust editions
               | 
               | like initially it might seem easy to introduce syntax
               | braking changes (which most rust edition changes boil
               | down to) but then you realize that build units using
               | other editions have to be able to read the header file
               | and the header file e.g. in context of templates can
               | contains any kind of code and that header includes aren't
               | that much different too copy pasting in the header and
               | that you don't have a standard package manager which can
               | trace which edition a header has and endless different
               | build systems and you kinda give up
               | 
               | purely technically it _is fully possible to have rust
               | like editions in C++_ but practically/organizationally in
               | context of e.g. backward compatibility with build systems
               | it's just way to disruptive to be practical
        
               | 0xffff2 wrote:
               | > When that is not required for the project at hand, we
               | happily reach out to C#, D, Java, Go, Rust, Zig, Swift,
               | Odin,.... instead.
               | 
               | Which is all well and good for us, the application
               | developers. But if C++ wants to exist in the future as a
               | thriving language (as opposed to moving in to the nursing
               | home with Cobol and Fortran), then it needs to come up
               | with some solution to remove cruft.
        
               | pjmlp wrote:
               | Until those languages decide to be fully bootstraped,
               | alongside Khronos and OpenGroup being welcoming to them
               | for newer standards, C++ won't go away.
        
               | Someone wrote:
               | It has a solution: obsoleting features and then removing
               | them. For examples, see
               | 
               | https://en.wikipedia.org/wiki/C%2B%2B17#Removed_features
               | 
               | https://en.wikipedia.org/wiki/C%2B%2B20#Removed_and_depre
               | cat...
               | 
               | https://en.wikipedia.org/wiki/C%2B%2B23#Removed_features_
               | and...
               | 
               | Part of their 'problem' is that they have lots and lots
               | of users with long-living code bases. That means that, if
               | they move fast and break things, their users won't move
               | to newer versions of the language.
               | 
               | Another part is that they want to be able to generate the
               | fastest code possible. That leads to such things as
               | having all kinds of constructors ('normal' ones, copy
               | constructors, move constructors), and giving developers
               | the ability to tweak them for maximum performance.
               | 
               | In addition, a lot of this was invented after the
               | language was in use for decades. I think that makes the
               | constructor story more complicated than needed.
        
               | estebank wrote:
               | > don't work with binary libraries,
               | 
               | None of the edition changes that Rust has made have any
               | effect on the ABI. It also has no stable Rust ABI, so
               | there wasn't an effort to formalize that, but 1) ABIs
               | should always be versioned (to avoid getting stuck with a
               | bad ABI) and 2) you can use editions for other kinds of
               | change in the meantime.
               | 
               | > across compilers,
               | 
               | This is almost tautological. Yes, having two C++
               | compilers agree to their support of editions is the same
               | as them agreeing to their support of concepts. I don't
               | see how this is a critique of the strategy.
               | 
               | > template code across editions, with semantic
               | differences.
               | 
               | Rust defines editions at the compilation unit level: the
               | crate. But it has macros (which are akin to templates)
               | and editions _are_ handled at that boundary (you can have
               | code in one edition invoke macros in another) because the
               | compiler tracks editions at the token level (the
               | information is attached to their Span). There 's no
               | reason editions in C++ can't work with templates. You
               | would have to specify the edition of the template, and
               | given C++ semantics you might have to have an opt-in
               | scope to say "use this edition here, override the rest of
               | the file", but it would be possible.
        
               | pjmlp wrote:
               | Rust editions are very conservative, expect everything to
               | be built from source, with the same compiler, and don't
               | touch semantic changes across versions, mostly grammar
               | changes.
               | 
               | Example, there is no story for scenario, where a callback
               | defined in one version, is used in another crate version,
               | calling into code, using yet another version, while
               | passing a closure with a specific type with semantic
               | changes across all versions.
               | 
               | I am not yet convinced they will scale at the size of
               | industry use cases for C and C++, with a plethora of
               | compilers, and mixing several editions on a 30 year old
               | codebase.
        
         | dataflow wrote:
         | They can't pull the rug out now, but I highly recommend making
         | your own clang-tidies to flag confusing constructs (like
         | defaulted out-of-line constructors) and preventing them from
         | being committed.
        
         | gosub100 wrote:
         | My guess is all these details are necessary to provide C++
         | "strong exception guarantee" against partially constructed
         | objects. Perhaps if your member objects can throw exceptions,
         | some of these pedantic initialization rules can come to the
         | rescue and allow, say, a library implementor to limit
         | initialization code to places where exceptions can be handled.
        
         | bun_terminator wrote:
         | True, however in practice this is rarely an issue. You usually
         | only use a tiny subset of construction rules. And if you ever
         | make a mistake, they are easily caught by static analysis
         | tools.
        
           | smackeyacky wrote:
           | It's quite a big issue. It's actually a bit worse than the
           | article makes out if you throw static objects into the mix
           | and get race conditions where you don't know which objects
           | get constructed first. C++ has to be approached with caution
           | even by experienced devs.
        
             | bun_terminator wrote:
             | experienced (and even the not so much) devs know the perils
             | of static initialization and avoid it.
        
             | maccard wrote:
             | I agree with the parent - global initialisation order is a
             | once-bitten-never-again, and the reality is that working in
             | most codebases doesn't require understanding all of these
             | rules - knowing the quirks is usually only required by one
             | or two people and the rest can work with what they've got.
        
             | TinkersW wrote:
             | that is a novice issue, it is easily avoided
        
         | dathinab wrote:
         | it being this complicated can be fine (if there isn't too much
         | of it)
         | 
         | but only if not knowing how the complicated parts work doesn't
         | create any subtle issues and has reasonable compiler time
         | errors and isn't fundamental needed to write any code
         | 
         | Because then you can use the language without needing to know
         | how exactly that complexity works and if you get it wrong you
         | get a reasonable compiler error. And then decide to either
         | spend some time to learn what you didn't know or you just write
         | the code differently. But in either case you don't have a
         | situation with a unexpected runtime error which should be
         | impossible and where you have no good way to know where to even
         | start looking.
        
         | jjmarr wrote:
         | Working as a junior in a C++ codebase is great for my career
         | because the skill floor is so high. Because it's so difficult
         | to do anything there's a lot of room to distinguish myself.
         | 
         | No other language creates as many problems for programmers as
         | C++.
        
       | wateralien wrote:
       | Upvote for the title.
        
       | gattilorenz wrote:
       | What an beautiful blog theme, obviously inspired by the DEC-era
       | computers but also clean and minimal. Refreshing!
        
         | imperialdrive wrote:
         | Ditto
        
         | georgestagg wrote:
         | I like how the rules to the right of the headings react to the
         | page width and how many lines are used.
        
       | kazinator wrote:
       | > _Otherwise, zero-initialize and then default-initialize._
       | 
       | That can't be right ... is it? Things cannot be initialized
       | twice. Isn't it more like "Otherwise, recurse the value-
       | initialization over the bases and members". Then, those that are
       | not classes or arrays get zero-initialized.
        
         | SpaghettiCthulu wrote:
         | I think it would be perfectly legal to zero-initialize the
         | entire thing and then default-initialize, because
         | initialization assumes the value is undefined.
        
           | bregma wrote:
           | You can only initialize once. After it's been initialized
           | you're just assigning values, and that's not what happens
           | during initialization. It's either a misunderstanding on
           | behalf of the author or the words as written are not
           | conveying the correct idea.
        
         | leni536 wrote:
         | Both "zero-initialize" and "default-initialize" are terms that
         | have precise definitions. In this context if you substitute the
         | definitions it just means first zero-initializing the non-
         | static data members (and zeroing the padding), then calling the
         | default constructor.
         | 
         | It doesn't mean that the lifetime of the object starts twice,
         | or anything weird like that.
        
       | 3l3ktr4 wrote:
       | This is the best title, OP.
        
       | gpderetta wrote:
       | > [...] The printed result would be 0. This is because we value-
       | initialize t and, since T has a non-user-provided default
       | constructor, the object is zero-initialized (hence t.x is zero-
       | initialized) then default-initialized (calling the implicitly-
       | defined default constructor, which does nothing).
       | 
       | That doesn't seem correct: a defaulted constructor still default-
       | initializes the members, not value initialize. I don't think
       | there is any difference between defaulting inline and out of
       | line. GCC seems to agree: https://gcc.godbolt.org/z/r4re5TE5a
       | 
       | edit: I missed that the author is actually value-initializing
       | x!!! The result definitely violates expectations!
       | 
       | Generally, the details of the rules are arcane and sometimes have
       | non-sensical dark corners having been extended and patched up for
       | the last 40 years. But 99.9%[1] of the time you get what you
       | expect.
       | 
       | I big improvement would be making default initialization
       | explicit, and otherwise always value initialize. Explicit value
       | initialization is so common that the very rare times I want
       | default initialization (to avoid expensively zeroing large
       | arrays) I need to write a fat comment. Writing "std::array<int,
       | 100> = void;" (or whatever the syntax would be) would be much
       | better.
       | 
       | [1] I had an extra 9 here... I hedged.
        
         | adrianN wrote:
         | Once every thousand lines you don't get what you expect? Rip
        
           | gpderetta wrote:
           | Once every 1000 initializations. But hey, I would sign up for
           | only one bug every 1000 lines.
        
       | hwc wrote:
       | I'm so glad I use Go more than C++ these days. In Go, all values
       | are always zero-initialized if you don't explicitly assign a
       | value. If you need a constructor, you write a regular function
       | that returns an explicitly assigned object.
       | 
       | I like keeping the rules of the language simple enough that there
       | is never any confusion.
        
         | javierhonduco wrote:
         | Personally I'm not a fan of Go's default zero-initialisation.
         | I've seen many bugs caused by adding a new field, forgetting to
         | update constructors to intialise these fields to "non-zero"
         | values which caused bugs. I prefer Rust's approach where one
         | has to be explicit.
         | 
         | That being said it's way less complex than C++'s rules and
         | that's welcomef.
        
           | ErikBjare wrote:
           | Haven't written Go in a long time, but I do remember being
           | bit by this.
        
           | zarathustreal wrote:
           | Yea this can be problematic if you don't have sum types, it's
           | hard to enforce correct typing while also having correct
           | default / uninitialized values.
        
           | maccard wrote:
           | I spent a year and a half writing go code, and I found that
           | it promised simplicity but there an endless number of these
           | kinds of issues where it boils down to "well don't make that
           | mistake".
        
             | gizmo686 wrote:
             | It turns out that a lot of the complexity of modern
             | programming languages come from the language designers
             | trying to make misaked harder.
             | 
             | If you want to simplyfing by synthesising decades of
             | accumulated knowledge into a coherent language, or to
             | remove depreciated ideas (instead of the evolved spaghetti
             | you get by decades of updating a language) then fine. If
             | your approach to simplicity is to just not include the
             | complexity, you will soon disciplinary that the complexity
             | was there for a reason.
        
           | crowdyriver wrote:
           | You can always use exhaustruct
           | https://github.com/GaijinEntertainment/go-exhaustruct
           | 
           | to enforce all fields initialized.
           | 
           | If you care, the linter is there, so this is more of a skill
           | issue.
        
           | catlifeonmars wrote:
           | FWIW there is a linter that enforces explicit struct field
           | initialization.
        
           | dgfitz wrote:
           | Wouldn't it just be considered bad practice to add a field
           | and not initialize it? That feels strongly like something a
           | code review is intended to catch.
        
             | javierhonduco wrote:
             | It's easy to miss this in large codebases. Having to check
             | every single struct initalisation whenever a field is added
             | is not practical. Some folks have mentioned that linters
             | exist to catch implicit initialisation but I would argue
             | this shouldn't require a 3rd party project which is
             | completely opt-in to install and run.
        
             | dieortin wrote:
             | All bugs are considered bad practice, yet they keep
             | happening :P
        
           | kccqzy wrote:
           | The problem you are describing in Go is rarely a problem in
           | C++. In my experience, a mature code base rarely has things
           | with default constructors, so adding a new field will cause
           | the compiler to complain there's no default constructor for
           | what you added, therefore avoiding this bug. Primitive types
           | like `int` usually have a wrapper around them to clarify what
           | kind of integers, and same with standard library containers
           | like vector.
           | 
           | However I can't help but think that maybe I'm just so
           | fortunate to be able to work in a nice code base optimized
           | for developer productivity like this. C++ is really a nice
           | language for experts.
        
             | dieortin wrote:
             | Why would you have a wrapper around every
             | primitive/standard library type?
        
         | thowruasdf wrote:
         | The downside is now all types must dogmatically have a nullary
         | constructor.
         | 
         | The Default typeclass (or trait) as seen in Haskell and Rust,
         | is a far better design, as this makes the feature opt-in for
         | data types that truly support them.
        
           | kccqzy wrote:
           | Don't even need to go that far. In C++ it is common to delete
           | the default constructor anyways. So that achieves the opt-in.
        
             | dieortin wrote:
             | Having to delete the default constructor means it's opt-
             | out, not opt-in
        
               | kccqzy wrote:
               | I don't think you really know what a real-life C++ code
               | base looks like. Deleting the default constructor often
               | happens implicitly with no syntax needed. This often
               | happens when you define a custom constructor or when its
               | member doesn't have a default constructor.
        
         | jeffbee wrote:
         | Go makes a completely reasonable tradeoff, giving away
         | performance to gain ease of use. C++ also makes a tradeoff that
         | seems reasonable or necessary to its users, that it makes it
         | possible to not be forced to write to the same class member
         | twice, in exchange for a more complex language. Any language
         | that attempts to offer this property unavoidably adopts the
         | complexity as well. See, for example, Java.
        
       | AlexandrB wrote:
       | For more C++ wackiness, I recommend the C++ FQA:
       | https://yosefk.com/c++fqa/
       | 
       | It's 15 years out of date now, but also timeless since C++
       | rarely/never removes old features or behaviours.
        
       | bookofjoe wrote:
       | >I Have No Mouth, and I Must Scream (1967)
       | 
       | https://talesofmytery.blogspot.com/2018/10/harlan-ellison-i-...
        
       | jolj wrote:
       | Is there a C++ tool that adds/shows all the implicit stuff that
       | happens behind the scenes?
       | 
       | Such as all the constructors that are being added, implicit copy
       | constructor and all the other surprises?
        
         | fouronnes3 wrote:
         | Best you're gonna get is a combination of godbolt and
         | cppinsights.
        
           | jolj wrote:
           | cppinsights looks like what I was looking for, there's even a
           | vscode extension thanks
        
       | sixtram wrote:
       | Great link, I'm going to add this to my list of favorite
       | interview questions. (^_-)
        
       | jeffbee wrote:
       | I agree there is a lot of complexity in C++ in the year 2024,
       | however I feel that much of the appearance of complexity around
       | initialization is due to the pedantic, dogmatic use of the word
       | "default" by the committee to mean "not".
        
       | amluto wrote:
       | I'm surprised there were no snarky comments about:
       | 
       | > So, here's the glue between list-initialization and aggregate
       | initialization: if list-initialization is performed on an
       | aggregate, aggregate initialization is performed _unless_ the
       | list has only one argument, of type T or of type derived from T,
       | in which case it performs direct-initialization (or copy-
       | initialization).
       | 
       | The word "unless" is even bold.
       | 
       | We have fancy syntax:                   T t{v0};
       | 
       | And we also have:                   T t{v0, v1};
       | 
       | And so on. But the one-element case does not reliably work like
       | the 2+-element case. And this is in a language that increasingly
       | works toward making it straightforward to create a struct from a
       | parameter pack and has support for variable length array-ish
       | things that one can initialize like this. And the types can, of
       | course, be templated.
       | 
       | So you can write your own constructors, and you can initialize a
       | tuple or array with only one element supplied, and you might trip
       | over the _wrong_ constructor being invoked in special cases.
       | 
       | I remember discovering this when C++11 initializer lists were
       | brand new and thinking it was nuts.
        
         | TinkersW wrote:
         | Initializer lists is irreverent, nobody uses it anyway. Other
         | than a few standard containers that use it, you can completely
         | ignore the silly thing.
        
           | alex_lav wrote:
           | As is the nature of bad design, "nobody uses it other than
           | some people sometimes" is a silly sentiment and indicative of
           | a problem.
        
           | Maxatar wrote:
           | That's the biggest problem with them. Something isn't as
           | dangerous when it's in your face and you need to confront it
           | on a regular basis. What's dangerous are the things that
           | rarely need to think about, except for that very rare moment
           | when it bites you in the ass precisely because it's not on
           | your mind.
        
       | vsgherzi wrote:
       | this has got to be one of my favorite blog names i've seen on
       | this site
        
       | pierrebai wrote:
       | The article is just plain wrong about classes: if you have
       | declared any constructor, then the language will _not_ provide a
       | default constructor and default-initialization will fail with a
       | compiler diagnostic.
       | 
       | So their claim that "T t;" will "do nothing" is incorrect.
       | class T         {         public:             T(int);         };
       | T t;
       | 
       | Will fail.
        
       | OptionOfT wrote:
       | T::T() = default;
       | 
       | > You'd expect the printed result to be 0, right? You poor thing.
       | Alas--it will be garbage. Some things can never be perfect, it
       | seems. Here's a relevant excerpt from our description of value-
       | initialization:
       | 
       | Link:
       | https://consteval.ca/2024/07/03/initialization/#:~:text=You%...
       | 
       | That actually isn't that weird, because it would allow any
       | consumer of your library to change how your library behaves.
        
       | nxobject wrote:
       | As an aside, I see that DEC front panel you've got there in your
       | blog header.
        
       ___________________________________________________________________
       (page generated 2024-07-05 23:01 UTC)