![]() |
|
![]() |
| Rule of 5/3/0 is about destructors, move and copy constructors. Apart from the last example, the article mainly talks about quirks with the standard constructor, if you can call it that. |
![]() |
| The alternative not making sense doesn't automatically make this solution make sense :( It just highlights how many corners C++ has backed its design into. |
![]() |
| 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 |
![]() |
| 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? |
![]() |
| 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... |
![]() |
| 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/Cl... |
![]() |
| I think people don’t like constructors because of the potential side effects of something happening in constructors, especially if the constructor is big or doesn’t finish properly. |
![]() |
| 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? |
![]() |
| 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. |
![]() |
| There are a number of standard libraries that we (Google) ban because we have in-house alternatives that we generally prefer. (In some cases, this is due to compatibility with our threading model. In others, it's probably due to inertia.)
From a skim: As far as language features that we ban, it's notable that we ban exceptions. Rvalue references are generally discouraged outside of well-trod paths (e.g. move constructors). We ban runtime type information (dynamic_cast, typeid, etc). There are some newer features in C++20 that we haven't accepted yet (modules, ranges, coroutines), due to lack of tooling, concerns about performance, and more. Sometimes these bans get reversed (there was a long-standing ban on mutable reference parameters that was overturned after many years of discussion). One of the key points we try to emphasize is that readability is primarily about reading other people's code, and so we provide well-trod paths that try to avoid pitfalls and generally surprising behavior (e.g. this article's focus on initialization? https://abseil.io/tips/88). There is value in moving closer to Python's "There should be one-- and preferably only one --obvious way to do it." We assert that one of the goals of the style guide (the second section, after Background) is that rules should pull their own weight; we explicitly cite this as a reason why we don't ban goto. I imagine this is also why there isn't an explicit ban on alternative operator representations (e.g. writing `if (foo and bar) <% baz(); %>`). I don't think I agree that every rule pulls its own weight -- I like to cite that I copy-pasted the internal version of the style guide into a Google doc earlier this year to see how long it was... and it clocked in at over 100 pages. But maybe that's an indicator of how complex C++ is, where we have to make the tradeoff between being concise, being precise, and providing sufficient context. |
![]() |
| Yes, modulo some text that's inappropriate to post externally. (Often, because they reference internal libraries in google3 -- this includes things like the ban on |
![]() |
| “External internal style guide” vs “internal external style guide”, both could make sense in different contexts.
Like I said, I never wrote c++ there so I’m quite likely misremembering details, but IIRC the published style guide linked to upthread is more or less a snapshot of the previously-unpublished style guide used internally for internal code. It may have some omissions and it’s quite likely out of date. I’m just amused that this discussion about semantics reminds me so much of the gif linked from https://news.ycombinator.com/item?id=40882247 |
![]() |
| But... all of the classes in the article -do- have default constructors. And all of the examples in the article do compile. So I'm confused at what point you guys are making. |
![]() |
| 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". |
![]() |
| 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. |
![]() |
| 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. |
![]() |
| 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. |
T̶h̶a̶t̶ d̶o̶e̶s̶n̶'t̶ s̶e̶e̶m̶ c̶o̶r̶r̶e̶c̶t̶:̶ a̶ d̶e̶f̶a̶u̶l̶t̶e̶d̶ c̶o̶n̶s̶t̶r̶u̶c̶t̶o̶r̶ s̶t̶i̶l̶l̶ d̶e̶f̶a̶u̶l̶t̶-̶i̶n̶i̶t̶i̶a̶l̶i̶z̶e̶s̶ t̶h̶e̶ m̶e̶m̶b̶e̶r̶s̶, n̶o̶t̶ v̶a̶l̶u̶e̶ i̶n̶i̶t̶i̶a̶l̶i̶z̶e̶. I̶ d̶o̶n̶'t̶ t̶h̶i̶n̶k̶ t̶h̶e̶r̶e̶ i̶s̶ a̶n̶y̶ d̶i̶f̶f̶e̶r̶e̶n̶c̶e̶ b̶e̶t̶w̶e̶e̶n̶ d̶e̶f̶a̶u̶l̶t̶i̶n̶g̶ i̶n̶l̶i̶n̶e̶ a̶n̶d̶ o̶u̶t̶ o̶f̶ l̶i̶n̶e̶. G̶C̶C̶ s̶e̶e̶m̶s̶ t̶o̶ a̶g̶r̶e̶e̶:̶ h̶t̶t̶p̶s̶:̶//g̶c̶c̶.g̶o̶d̶b̶o̶l̶t̶.o̶r̶g̶/z̶/r̶4̶r̶e̶5̶T̶E̶5̶a̶
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 = void;" (or whatever the syntax would be) would be much better.
[1] I had an extra 9 here... I hedged.