[HN Gopher] C++ by Example
       ___________________________________________________________________
        
       C++ by Example
        
       Author : synergy20
       Score  : 204 points
       Date   : 2022-09-22 12:52 UTC (10 hours ago)
        
 (HTM) web link (cppbyexample.com)
 (TXT) w3m dump (cppbyexample.com)
        
       | frozencell wrote:
       | They should do: C++ by Problems.
        
       | jdlyga wrote:
       | I always loved C++ as a language. Most of the use cases for it
       | nowadays are when performance is key, and people use python for
       | everything else. But I miss the structure that C++ provides
       | without the heavy hand that Java and C# require. I definitely do
       | not miss memory leaks and investigating core dumps though.
        
         | ncmncm wrote:
         | Good news, with modern usage, memory leaks are a thing of the
         | fast-receding past. Occasion to investigate core dumps is
         | lately vanishingly rare. (I have spent strictly more time
         | filing compiler bug reports, in recent years, than debugging
         | memory usage violations.)
         | 
         | Today, it is fun to code in C++. Restricting it to performance-
         | critical uses would be a bad mistake. Displacing C with
         | simpler, more reliable, often faster C++ is an easy choice, but
         | reaching for C++ in place of Python is also often a great idea.
        
           | TillE wrote:
           | Yeah I literally have not had a single memory leak since I
           | started using unique_ptr and shared_ptr everywhere.
           | 
           | I have had a couple resource leaks with C APIs, though even
           | they could have been avoided with unique_ptr and a custom
           | deleter.
        
         | [deleted]
        
       | scaredginger wrote:
       | Hmm, not sure I'd trust this. I took a look at the shared_ptr
       | page and didn't see it mention reference cycles or weak_ptr
       | 
       | https://cppbyexample.com/what_is_shared_ptr.html
        
         | MauranKilom wrote:
         | The site is very clearly not a complete guide, and has to leave
         | out 90% of what a competent C++ programmer _should_ know about
         | any given topic (in order to remain accessible). I mean, the
         | word  "undefined" only appears in two of the examples.
         | 
         | But I'd say it doesn't teach any _bad_ (or outdated) habits,
         | which is a big step up from most tutorials /articles/SO
         | answers.
        
         | planede wrote:
         | I have an other gripe with that page:
         | 
         | > [about std::make_shared] This function is useful because
         | objects might throw exceptions during construction and if they
         | do we still need to call delete on the pointer we received from
         | new otherwise we will leak the pointer.
         | 
         | This is oversimplified and just plain incorrect. There was
         | never a possibility that a statement like `auto sh_ptr =
         | shared_ptr<Widget>(new Widget);` could leak.
         | 
         | However possibility for leak existed for calls like
         | `foo(shared_ptr<Widget>(new Widget), shared_ptr<Widget>(new
         | Widget))`, because evaluation of function arguments were
         | unsequenced. This hole was fixed in the language in C++17,
         | where function arguments became indeterminately sequenced.
         | 
         | And possible advantage for `make_shared` is the shared
         | allocation for the control block and the object. It is not a
         | clear advantage, as remaining `weak_ptr`s keep the whole
         | allocation alive, even after the object itself is destroyed.
         | But I wouldn't include this to a tutorial.
         | 
         | One annoying place where you can't use `make_shared` and
         | `make_unique` is for invoking private constructors, even in a
         | context where you could use the private constructor directly
         | (like a static factory function within the same class). Since
         | `make_shared` and `make_unique` are typically not friends of
         | your class, they can't call the constructor indirectly. There
         | are some workarounds for this, but the easiest way is just to
         | use `new` here, it's fine.
        
           | deadbeeves wrote:
           | It seems more robust to just make std::make_*() a friend
           | function than to pepper the code with news.
        
             | MauranKilom wrote:
             | That only works if `std::make_*` is actually where `new` is
             | called. It could just as well be called deeper in some
             | library implementation detail like
             | `std::__gnu_cxx_v3::__construct_as(...)` (made up example),
             | in which case you're SOL.
        
               | deadbeeves wrote:
               | Hmm... Good point. I don't think it's a portable
               | solution.
        
             | planede wrote:
             | I agree. If you can, just use the make_* functions. It also
             | reduces needless repetition. For `make_*<Widget>(args...)`
             | I need to write the type once, for `*_ptr<Widget>(new
             | Widget)` I need to write the same type twice. Not counting
             | cases for `Base/Derived`.
        
       | ur-whale wrote:
       | Where's the template metaprogramming section?
       | 
       | What about the double bracket operator?
       | 
       | etc...
       | 
       | Or, to put it another way: very nice website for basic exposure
       | but mostly demonstrates very basic stuff.
        
         | donedealomg wrote:
        
         | sudosysgen wrote:
         | I don't see what special about double brackets? It's just
         | calling the operator again on the result, completely intuitive.
        
           | edflsafoiewq wrote:
           | He means attributes, eg [[nodiscard]]. Of course you
           | shouldn't call them operators.
        
             | MauranKilom wrote:
             | See, I assumed they meant the >> operator. That used to
             | (long ago) be cause for grief in e.g.
             | std::vector<std::vector<int>> where the parser would see a
             | >> operator instead of two closing angle brackets (since
             | fixed by changing the grammar).
        
               | tialaramex wrote:
               | So, there are four things which might get colloquially
               | called "Brackets", they each have other names but...
               | 
               | (Round) brackets are Parentheses
               | 
               | {Curly} brackets are Braces
               | 
               | <Angle> brackets are Chevrons
               | 
               | [Square] brackets are often just Brackets
               | 
               | Ah, so there's the problem. Now, this actually also
               | differs depending on English variant. In England it would
               | be pretty normal to say "Brackets" and mean Parentheses,
               | whereas in the US you'd mean the _square_ brackets. Some
               | English speakers outside the US might call the Square
               | Brackets  "Crotchets" to distinguish them, not so many.
               | US English dominates in technology.
        
         | jvanderbot wrote:
         | Yeah, it's fairly basic, and most of this is in the usual c++
         | docs like cppreference.com
         | 
         | But... Is template meta-programming a required feature of a C++
         | dev?
         | 
         | double-bracket is ... an edge case of usual [] overloading,
         | isn't it?
        
           | ur-whale wrote:
           | These are just examples to make a point.
           | 
           | And: https://arne-mertz.de/2016/12/modern-c-features-
           | attributes/
        
           | ncmncm wrote:
           | It has been years since I was tempted to template
           | metaprogramming. It is a specialist technique being made
           | redundant by straightforward language features that compile
           | much faster.
        
           | stonemetal12 wrote:
           | Templates are a major feature of the language, leaving it out
           | seems a little weird.
        
             | sva_ wrote:
             | These? https://cppbyexample.com/templates.html
        
               | ur-whale wrote:
               | That's just templates.
               | 
               | I'm talking about stuff like this:
               | 
               | https://en.wikipedia.org/wiki/Template_metaprogramming#St
               | ati...
        
             | d_tr wrote:
             | Looks like an ongoing work so more will probably be
             | available in the future. I see an example for templates.
             | Template metaprogramming is more advanced but would be nice
             | to include.
        
       | DethNinja wrote:
       | So far I've used six languages to build everything from web apps
       | to desktop applications and C++ is still by far my favourite
       | language.
       | 
       | It is sad that it is still haunted by prejudice due to pre-C++11
       | era but modern C++ is easy to learn and use, and I recommend
       | every programmer should give it a try.
        
         | pjmlp wrote:
         | The problem is that many keep teaching C++ with code that a C
         | compiler won't have any issues with.
         | 
         | That is why Bjarne keeps doing those advocacy talks about best
         | practices.
        
           | ModernMech wrote:
           | The real problem as I see it is that people think the path to
           | safe C++ is to tell everyone "you're holding it wrong" (just
           | use modern C++), when it fact being able to hold it wrong is
           | regarded as a feature (C++ maintains backward compatibility)
           | by the C++ community.
        
             | pjmlp wrote:
             | By C expats on C++ country.
        
           | mk89 wrote:
           | Related to this, maybe: an issue I had was also the fact that
           | online many of the help pages are really outdated.
           | 
           | I can't forget an old accepted "much voted" answer I saw on
           | stackoverflow which claimed that raw pointers should be
           | owning resources, because that's how the language always did.
           | 
           | I think the answer was dated like 2008 or so, can't find the
           | reference anymore. That's when I started to pay a lot more
           | attention to the published date whenever I search for help
           | and I stumble upon a very old article. If only we could get
           | rid of old resources written before maybe 2018-2019 and only
           | select those that are still "modern", that'd be already a
           | great step forward.
           | 
           | Luckily we have this now [0].
           | 
           | They should invest more in tools like the GSL extension at
           | least as a clang warning which really helps, and things like
           | that. That's how I believe Java was also able to modernize.
           | You can write pretty crappy Java too, but tools like findBugs
           | or checkstyle do help, so why not!
           | 
           | [0]: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidel
           | ines...
        
             | pjmlp wrote:
             | That is what using VC++ with integrated support for Core
             | Guidelines feels like, naturally that is a minority.
             | 
             | https://learn.microsoft.com/en-us/events/pure-virtual-
             | cpp-20...
        
               | mk89 wrote:
               | Yeah, I kind of envy that.
               | 
               | Although with Clang-tidy I sometimes see GSL warnings:
               | https://clang.llvm.org/extra/clang-tidy/ which is pretty
               | cool - I wonder how up-to-date that is compared to the
               | GSL extension.
               | 
               | The more you invest in this kind of automated tools, the
               | earlier you catch such bugs. Sure, sometimes it's "false
               | positives, I know what I am doing", but you cut maybe a
               | good > 90% of crap that comes from just writing old-style
               | code.
        
       | threatripper wrote:
       | In Firefox the code text is only readable in dark mode. In Light
       | mode it's dark grey on black with a bright page around it.
        
       | photochemsyn wrote:
       | Nice educational resource. Things like this help a lot relative
       | to cppreference which often assumes the reader knows many things
       | already. For example:
       | 
       | https://cppbyexample.com/what_is_unique_ptr.html
       | 
       | https://en.cppreference.com/w/cpp/memory/unique_ptr
       | 
       | The former spells out the rationale for the design - such as why
       | you might need get or why make_unique is better than new, and why
       | copies are forbidden but moves are allowed - that the official
       | reference sort of assumes the reader already understands.
        
       | MauranKilom wrote:
       | I like it. It uses uncomplicated language, doesn't try to
       | frontload you with the complexity beneath, and shows clean and
       | well-principled code (e.g. no `using namespace std;` - they say
       | that all code snippets compile).
       | 
       | And I say this as someone who gets a kick out of template
       | metaprogramming.
       | 
       | Yes, you will commit atrocities if you try to learn C++ from
       | _just_ this resource. It is very much a nice facade around the
       | bottomless pit that is C++. But in terms of getting someone
       | started on their first small C++ projects, this is extremely to-
       | the-point, and infinitely better than requiring people to dig
       | through  "how do I read a file" StackOverflow answers or
       | needlessly elaborate articles that show terrible code "for the
       | sake of exposition".
       | 
       | It might go against the intent of the site, but some kind of
       | notice like "C++ is big and has _many_ sharp edges, so you should
       | eventually look to curate your understanding beyond this site "
       | would round it out.
        
       | exisvr wrote:
       | Cool site! I rarely use C++ now, but I still miss C++ from time
       | to time!
        
       | arberx wrote:
       | As my first language, I've yet to experience another language
       | that genuinely lets me be as creative as C++.
       | 
       | There are hundreds of ways to implement and optimize similar
       | functionality that I feel like an artist writing in it. This, of
       | course, comes with many downsides.
        
         | a3w wrote:
         | How do you fell about the pitfalls, like "if you inherit from a
         | class, make sure the base destructor is virtual, or you just
         | added a memory leak"
         | 
         | Some books on C++ teach you those, but I give up very early on
         | learning languages with non-obvious expectations that offers
         | few safeguards.
        
           | tejohnso wrote:
           | You'd want to read more than one book. And in my opinion you
           | also need to digest the C++ Core guidelines and also use
           | something like clang-tidy with high sensitivity, and when
           | issues are flagged go dig into the details.
           | 
           | But then you realize it's just all too much, and you're
           | looking for something less complex again.
           | 
           | I wonder how useful a C++ that I could understand in its
           | entirety would be. But I don't understand english in its
           | entirety either. I use what I know and get by just fine.
        
           | rramadass wrote:
           | That is because C++ is not taught properly. It is not just a
           | collection of syntactical features but a multi-paradigm
           | language (i.e. supporting Procedural, Object-Oriented,
           | Generic and Compile-time) where a feature is best suited to a
           | particular paradigm and can also be used to express different
           | concepts. Once you grasp this, things become quite clear and
           | you _understand how to design in C++._
           | 
           | To take your example; the same C++ "class" syntax can be used
           | to express different concepts;
           | 
           | a) A user-defined value type eg. ComplexNumber. Therefore the
           | dtor need not be virtual since the class is not designed for
           | inheritance.
           | 
           | b) A pure interface used for "Interface Inheritance" i.e. all
           | methods are "pure virtual". Therefore you need a pure virtual
           | dtor. Java has the "Interface" keyword for this.
           | 
           | c) A class designed for a type hierarchy used for
           | "Implementation Inheritance". Therefore you need a virtual
           | dtor.
           | 
           | d) A class can also be used as a simple namespace eg. all
           | static members; not designed for inheritance and thus no dtor
           | needed.
        
             | bogomipz wrote:
             | >"That is because C++ is not taught properly. It is not
             | just a collection of syntactical features but a multi-
             | paradigm language (i.e. supporting Procedural, Object-
             | Oriented, Generic and Compile-time) where a feature is best
             | suited to a particular paradigm and can also be used to
             | express different concepts. Once you grasp this, things
             | become quite clear and you understand how to design in
             | C++."
             | 
             | Might you or someone else recommend a resource that does
             | explain which features are suited for each the programming
             | paradigms C++ support? Is there a modern resource that does
             | teach it correctly like this? Knowing what features are
             | relevant to your paradigm seems like a good way to approach
             | learning C++.
        
           | Chabsff wrote:
           | That's not even particularly good advice, which just goes to
           | show how subtle the pitfalls are. The rule really should be
           | "If it is at all possible to delete instances from a pointer
           | to the base class, then you need a virtual destructor". The
           | overhead of adding a virtual destructor to a base class with
           | no other virtual functions is far from negligible in many
           | cases, so the distinction is meaningful (though modern
           | guidance tends discourage non-polymorphic inheritance in the
           | first place anyways).
           | 
           | The same thing goes for the good-old "Do not use raw
           | pointers." The rule is actually: "Do not use raw pointers
           | with implicit ownership semantics attached to them."
        
             | nyanpasu64 wrote:
             | In retrospect I wish C++ used fat pointers rather than
             | inheritance for virtual dispatch, like Rust but where
             | interfaces/traits can have fields (Rust traits don't have
             | fields and are less powerful than C++ base classes). This
             | prevents accidentally overriding a base class method (eg.
             | QWidget) by declaring a subclass method of the same name,
             | eliminates the need to add a vtable pointer to object
             | instances which you never cast to an interface pointer, and
             | eliminates the need to juggle pointer offsets when
             | performing multiple inheritance method calls, which can
             | cause UB when performing C-style pointer casts and makes
             | decompiling the code a nightmare (possibly fields in traits
             | will add this requirement again, I'm not sure).
        
           | ncmncm wrote:
           | Inheritance is a specialist technique, use of virtual members
           | moreso. Most destructors in modern C++ are left to the
           | compiler to generate.
           | 
           | It has been years since I last coded a virtual destructor.
        
           | kazinator wrote:
           | Speaking of inheritance, how do you feel about this not
           | requiring any diagnostics. It produces a weird result without
           | containing any cast notation to subvert the type system.
           | $ g++ -Wall -W -Wextra deriv.cc -o deriv       $ ./deriv
           | 1.08654e-10            $ cat deriv.cc       #include <cstdio>
           | struct Base {       public:         double y;         Base()
           | : y(1.0) {}       };            struct Derived : public Base
           | {       public:         int a, b;         Derived() :
           | a(0x3DDDDDDD), b(0x3DDDDDDD) {}       };            int
           | main()         {         Base *b = new Derived[5];
           | b++;         std::printf("%g\n", b->y);       }
        
             | jahnu wrote:
             | For me this is the least of c++'s problems. Today I
             | wouldn't let that past a review as it should be using a
             | std::array. If a c++ dev has a reason to drop down to this
             | unsafe code they should know what the consequences are.
        
               | [deleted]
        
         | kazinator wrote:
         | There are hundreds of ways to do something, each one more
         | importunely verbose and loaded with extraneous syntax than the
         | next.
        
         | Chabsff wrote:
         | And this half of my main beef with the language. To be clear, I
         | absolutely agree that this aspect of it is wonderful, but it is
         | a double-edged sword, and the edge pointing at the programmer's
         | face is very sharp.
         | 
         | The second half of that beef is that most C++ learning journeys
         | bring the learner up to the point of creativity without
         | addressing concepts like Strict Aliasing which are not useful
         | until you start getting "creative". That's big a problem
         | because code containing strict aliasing violations (and/or any
         | of a myriad of other UB) is often fundamentally broken while
         | giving the illusion of behaving as intended.
        
           | synergy20 wrote:
           | or use -fno-strict-aliasing and profile the code to lift it
           | for bottleneck code carefully? linux kernel does not allow
           | strict-aliasing for example. but I agree yes all c and c++
           | course should teach strict aliasing.
           | 
           | in modern c++, also teach RAII and unique_ptr, combined they
           | can eliminate so many memory safety problems.
        
             | Chabsff wrote:
             | C++ without strict aliasing is not C++ anymore.
             | Furthermore, that was just an example. There are other such
             | concepts, such as the illegality of accessing memory as
             | anything else than raw bytes or the type of the object that
             | resides there (which also applies to union fields). Another
             | one would be the fact that signed integer overflow is UB.
             | 
             | C++ is so tightly bound to memory layouts that once you
             | start getting a solid mental model of what's going on, it's
             | _really_ easy to come up with legitimately neat and
             | creative ideas that work very well and fast on a test bench
             | environment without any complaint from modern compilers,
             | but are fundamentally broken. I feel we do not do a good
             | job at equipping up-and-coming programmers so that they can
             | benefit from that mental model without writing code that
             | only works out of sheer luck.
             | 
             | In the extreme, you can write code that does stuff like
             | dynamically retype an object by swapping out its vtable
             | when vtables are not even thing as far as the language is
             | concerned. It's _astounding_ what it sometimes feels like
             | you are capable of doing in C++.
        
               | kazinator wrote:
               | > _C++ without strict aliasing is not C++ anymore_
               | 
               | That is completely false.
               | 
               | Firstly, a C++ implementation that doesn't optimize based
               | on no aliasing assumptions can be entirely conforming: it
               | can handle all correct, portable programs in the required
               | way.
               | 
               | Secondly, the handling for programs which do type punning
               | doesn't make it not C++ anymore; just a dialect. Programs
               | in that dialect are C++, just not ISO C++.
               | 
               | Every implementation of a standardized language is a
               | dialect of that language; the standard just explains what
               | is the common dialect (theoretically) supported by all of
               | them.
               | 
               | In fact it's common for C and C++ compilers to recognize
               | their own dialect by default.
        
               | arcticbull wrote:
               | > That is completely false.
               | 
               | I suppose but only because C++ is everything and nothing.
               | C++ compilers are borderline self aware because the spec
               | is 'write literally whatever you want'. Skynet was
               | obviously a C++ compiler ;)
        
               | kazinator wrote:
               | The meaning of punning memory is obvious if you
               | understand the implementation-specific representations
               | being used. E.g. if you understand what a 64 bit double
               | looks like and what a 64 bit integer looks like, then it
               | makes sense when you look at the memory of one through
               | the type of the other. The C or C++ program to it has
               | only two rationally defensible meanings: "undefined
               | behavior" (ISO C or C++ interpretation, allowing certain
               | optimizations, or run-time diagnosis of a type mismatch
               | or whatever) or else "do the obvious thing and access the
               | bits like the code says". What the code is asking for can
               | certainly be be framed in terms of concepts in the
               | standard. Just what the code is obviously requesting is
               | not granted by any requirements in the standard. The idea
               | that by asking for it, it's not a C++ program any more is
               | silly.
               | 
               | There is no standardized programming language that
               | doesn't have vendor specific extensions comprising a
               | local dialect.
        
               | synergy20 wrote:
               | I understand compiler likes strict aliasing for
               | performance optimizations, use it carefully but _only_ at
               | bottleneck-ed areas seems like a good trade-off between
               | safety and performance, why without-it is no c++ anymore?
               | why do I need do pointer aliasing randomly?
        
               | Chabsff wrote:
               | Strict aliasing is what prevents compilers from having to
               | sprinkle Load-Hit-Stores all over the place. This is not
               | a marginal performance difference, and doubly-so for CPUs
               | with deep execution pipelines. It also has _massive_
               | code-reordering implications.
               | 
               | In any case, Strict Aliasing is not a flippant rule that
               | compilers like to apply. It's a formal component of the
               | ISO standard that C++ is.
               | 
               | You can chose to make a deal with a compiler (via a flag)
               | to compile otherwise non-compliant code in a consistent
               | way, but that does not change the fact that the code is
               | non-compliant.
        
               | kazinator wrote:
               | There are ways of coding coding in C which makes those
               | optimizations unnecessary.
               | 
               | The basic idea is: don't load your block of code with
               | lots of pointer dereferences. Load the values you need
               | into local variables. Don't proliferate common
               | expressions which dereference the same pointer to get at
               | the same value. Consolidate the assignments through
               | pointers. Don't do this in three places: (*ptr)++. Have
               | that value in a local variable var, and do var++ in three
               | places, then assign it *ptr = var.
               | 
               | Even without no-strict aliasing, a compiler can still
               | assume that local variables whose addresses are not taken
               | are not the targets of any pointers.
               | 
               | In C (99 or later), you have _restrict_ also. _restrict_
               | is independent of strict aliasing, because it 's not
               | based on type.
               | 
               | Furthermore, speaking of restrict, aliasing between _like
               | typed_ objects matters for optimization. It 's good and
               | well to optimize based on the idea that a _double *
               | cannot be aiming at an object whose declared type is_
               | long _. But it 's insufficient, because code that is
               | manipulating _double * pointers is likely working with
               | objects of type _double_ which could be the targets of
               | those pointers. So without some combination of tight
               | coding and possibly using restrict, you will end up with
               | those load-hit-stores in all sorts of code.
               | 
               | This code for removing from a linked list:
               | node->prev->next = node->next;        node->next->prev =
               | node->prev;
               | 
               | still isn't as good as this:                  node *next
               | = node->next;        node *prev = node->prev;
               | next->prev = prev;        prev->next = next;
               | 
               | The problem is that the common expressions can't be
               | eliminated based on aliasing, and the aliasing is between
               | like types, so strict aliasing doesn't help.
               | 
               | With gcc (x86) I get:                       movl
               | (%eax), %edx             movl    4(%eax), %ecx
               | movl    %ecx, 4(%edx)             movl    4(%eax), %eax
               | movl    %eax, (%eax)
               | 
               | versus:                       movl    (%eax), %edx
               | movl    4(%eax), %eax             movl    %eax, 4(%edx)
               | movl    %edx, (%eax)
               | 
               | We shaved off an instruction through tighter coding, and
               | IMHO (in this case) improved the readability also.
               | 
               | That was gcc 7 on Ubuntu. The result is exactly the same
               | like what I saw under gcc 2.7.x a quarter century ago.
               | 
               | How about gcc 11, x86-64? I should probably be using
               | godbolt, but anyway, also five instructions down to four:
               | movq    (%rdi), %rax             movq    8(%rdi), %rdx
               | movq    %rdx, 8(%rax)             movq    8(%rdi), %rax
               | movq    %rax, (%rax)
               | 
               | vs:                       movq    8(%rdi), %rax
               | movq    (%rdi), %rdx             movq    %rax, 8(%rdx)
               | movq    %rdx, (%rax)
               | 
               | (I think in this particular case the compiler could do a
               | better job because even if the assignment
               | "node->prev->next = node->next" clobbers the value of
               | "node->next" due to the nodes being aliases, the
               | assignment can only clobber it with the value that
               | node->next already has! The compiler doesn't analyze it
               | that far though.)
               | 
               | C was designed from the start as a language which the
               | programmer does the optimizing, and regardless of the
               | advancements in compilers, that has not been entirely
               | eliminated.
               | 
               | How you write C still makes a difference, even at the
               | microscopic level of individual statements and
               | expressions, not just the level of overall program
               | organization and use of algorithms.
               | 
               | If you write tight code, you can turn off strict aliasing
               | optimizations globally and it won't matter. But you don't
               | have to do that globally. You may be able to confine your
               | type punning hack in its own source file, and just turn
               | it off for that file. (Or may be even on a finer
               | granularity if you have such compiler support.)
        
               | stoeckley wrote:
               | I remember once telling a friend of mine, after I'd spent
               | several years writing Clojure full-time, that upon
               | getting back into writing C++, the feeling was like
               | "sculpting with electricity." There is a strange sense of
               | power you feel from harnessing the language in a creative
               | way, unlike any other language I've used.
        
               | kazinator wrote:
               | Did he mean with rubber gloves and insulated mat, or
               | without?
        
               | [deleted]
        
           | rramadass wrote:
           | https://blog.sigplan.org/2021/11/18/undefined-behavior-
           | deser...
        
             | Chabsff wrote:
             | Just in case you posted this as a counterpoint to what I'm
             | saying...
             | 
             | This blog effectively agrees with my position: Undefined
             | Behavior is just "Stuff the compiler is allowed to assume
             | never happens". And C++'s set of Undefined Behaviour is a
             | fundamental part of what makes it valuable.
             | 
             | The issue I have is not with them, but rather with the fact
             | that it's too easy to become competent, if not talented,
             | way past the point where one should take them into account
             | without having to even know of their existence.
        
         | volsa_ wrote:
         | As someone who tried learning C++ a few years ago this is
         | (personally) my biggest gripe with the language and this gif[0]
         | perfectly sums it up. Gave up and started learning Rust
         | instead, which I'm very happy about in hindsight.
         | 
         | [0] https://twitter.com/timur_audio/status/1004017362381795329
        
           | ncmncm wrote:
           | Fortunately, those can almost all be ignored almost all the
           | time.
           | 
           | Rust will seem fine until you are writing a library and find
           | no way to express what would make the library nicer to use;
           | or you find using some library awkward and error-prone.
           | Almost every C++ feature is designed to enable delivering
           | more powerful libraries, and they compound.
           | 
           | So, in C++ you as library writer are empowered to make the
           | use of your library correct by construction: what compiles is
           | safe and correct. Rust makes the compiler responsible for
           | _memory_ safety, but offers much less to make using your
           | library pleasant and foolproof.
        
             | volsa_ wrote:
             | > but offers much less to make using your library pleasant
             | and foolproof
             | 
             | Not my experience so far, I might go as far and say it
             | makes it more foolproof because when writing your library
             | you have the option to return an `Option` or `Result` enum.
        
               | ncmncm wrote:
               | Option and Result types are as easily coded in C++.
               | 
               | The former is in the Standard C++ library. The latter
               | will be in the next, but anyway the one in Boost has
               | worked fine forever.
               | 
               | Throwing, instead, is a choice unavailable to Rust
               | coders, so you often have no alternative but to return
               | these more complicated things that users are then obliged
               | to unpack. A standard macro makes that easier, and is
               | semantically equivalent to throwing, but imposes
               | substantial overhead on successful calls.
        
               | Rusky wrote:
               | C++'s option and result types are much larger, uglier,
               | and harder to use correctly than Rust's. The shift from
               | throwing to returning a Result is not really an increase
               | in complexity- closer to a reshuffling of syntax.
               | 
               | But that's not really all that relevant either, because
               | comparing languages at this level of detail misses the
               | forest for the trees. An interesting comparison takes a
               | step back and compares which problems the languages (try
               | to) solve. Often something that appears to be a clear win
               | for one or the other turns out to be a non-issue.
        
               | arinlen wrote:
               | > _C++ 's option and result types are much larger,
               | uglier, and harder to use correctly than Rust's._
               | 
               | What's your rationale for claiming in a swiping
               | generalization that implementing a data type, regardless
               | of all options or approaches and design, has no other
               | option than doing everything wrong?
        
               | Rusky wrote:
               | I mean, have you looked at them?
               | 
               | C++'s standard library optional type is a tagged union
               | implemented from scratch, with a dizzying array of
               | template metaprogramming to meet the standard's
               | requirements- see e.g. Microsoft's here: https://github.c
               | om/microsoft/STL/blob/main/stl/inc/optional#.... This
               | isn't a criticism of the team behind this code, it's just
               | what it takes to do this in C++.
               | 
               | Rust gets most of that functionality from the language
               | instead, in a much cleaner way. Sum types are built in,
               | value categories and move semantics are handled
               | automatically, there is little-to-no metaprogramming, and
               | most of the API is just simple convenience combinators
               | with obvious implementations: https://github.com/rust-
               | lang/rust/blob/master/library/core/s...
               | 
               | If nothing else, this is a clear counterexample to the
               | claim that "Option and Result types are as easily coded
               | in C++."
        
               | ncmncm wrote:
               | > _it 's just what it takes to do this in C++_
               | 
               | False.
        
               | LegionMammal978 wrote:
               | Do you have any examples of how std::optional could be
               | implemented more simply?
        
               | arinlen wrote:
               | > _I mean, have you looked at them?_
               | 
               | Last time I looked at Rust's std::Result, it's
               | implemented as a tagged union.
               | 
               | Most of C++'s implementations of a Result data type are
               | implemented as tagged unions as well.
               | 
               | What's your point?
               | 
               | > _This isn 't a criticism of the team behind this code,
               | it's just what it takes to do this in C++._
               | 
               | The only conceivable argument you can possibly make is
               | argue that Rust might support tagged unions as language
               | primitives, but that would be totally pointless as Result
               | types are relevant for the interfaces and higher level
               | abstraction they provide, not your personal opinion of
               | how hard someone else had to work to implement them.
               | 
               | I've developed Result and Either types in a few
               | languages, including C++, and the only hard thing about
               | C++ is doing the usual homework to handle
               | lvalur/revalue/revalue well.
        
               | ncmncm wrote:
               | The substantial performance overhead, in Rust, of code to
               | handle those types is unavoidable, even where source-
               | level inconvenience has been minimized.
               | 
               | Overhead is imposed all up and down the call chain, every
               | time through. Seems pretty relevant to me.
        
               | mathstuf wrote:
               | The fact that `std::optional<T&>` is verbotten makes them
               | quite a bit more useless. The Boost one is _better_
               | because it doesn 't have this misguided limitation. It's
               | really quite a sad story and a loss to all of C++ that it
               | is this way (and not for lack of trying on ThePhD's
               | part).
               | 
               | > no alternative but to return these more complicated
               | things that users are then obliged to unpack
               | 
               | Yes, thank you. It is much better this way. I think
               | "substantial overhead" needs to be backed up with some
               | numbers here because setting up exception landing pads is
               | certainly not free.
        
             | mathstuf wrote:
             | > So, in C++ you as library writer are empowered to make
             | the use of your library correct by construction: what
             | compiles is safe and correct
             | 
             | Empowered? Sure. But it seems no one likes using that power
             | for long stretches of time. Rust gives the same power and
             | it gets used far more usefully IME.
             | 
             | > Almost every C++ feature is designed to enable delivering
             | more powerful libraries, and they compound.
             | 
             | Well, we can take `std::variant`, `std::optional`, and
             | `std::expected` out of that pool because the committee
             | hamstrung them from their better alternatives that already
             | existed in Boost for silly, misguided reasons.
             | 
             | > Rust makes the compiler responsible for memory safety,
             | but offers much less to make using your library pleasant
             | and foolproof.
             | 
             | C++ libraries have, historically IME, been far more likely
             | to ask you to juggle live grenades while dancing in a
             | minefield than Rust libraries and APIs have.
        
             | oconnor663 wrote:
             | I think Rust's combination of data-bearing enums and
             | destructive move semantics goes a really long way towards
             | letting library authors express their invariants through
             | the type system. And that's in addition to the borrow
             | checker and mutable aliasing rules. The mutable aliasing
             | rules in particular are often kind of annoying for callers,
             | but they're a godsend for API authors, because they allow
             | for methods like Mutex::get_mut that don't even need to
             | lock the Mutex, in safe code!
        
           | alfiedotwtf wrote:
           | Same here... I got through Stroustrup and thought I'm not
           | smart enough to write this error free. Been happy with Rust
           | ever since.
        
             | cyber_kinetist wrote:
             | It's all fun and games until you find the need to unsafe
             | Rust, which opens up a bunch of eldritch monstrosities that
             | are sometimes even harder to tame than C++.
             | 
             | The happiness people get from Rust was achieved by the
             | incredibly hard work of library developers who had to deal
             | with all sorts of complex semantics under the hood to make
             | safe Rust safe. The knowledge gap between library user and
             | library writer is even worse than C++, where most people do
             | not feel comfortable enough to actually write low-level
             | libraries (custom data structures, bindings from C,
             | optimized routines) in Rust.
             | 
             | (At least PL people are inventing things like stacked
             | borrows to make writing unsafe Rust easier... but it's
             | still not easy.)
        
               | unrealhoang wrote:
               | on the other hand, that makes Rust's learning curve way
               | way more gradual than C++. An intermediate programmer
               | with 3 months of Rust experience can reliably and
               | confidently cobble up the libraries together to
               | contribute to the project. Whereas such programmer will
               | be a heavy burden (for other seasoned devs to
               | guide/review/feedback) in a C++ project.
               | 
               | Also, building low-level libraries that work correctly,
               | efficiently, relatively-safely in C++ is NOT a simple
               | feat, and requires years of experience (on API design).
               | With unsafe Rust you can just fuzz/brute force out the
               | unsoundness with the safe API.
        
               | spoiler wrote:
               | > It's all fun and games until you find the need to
               | unsafe Rust, which opens up a bunch of eldritch
               | monstrosities that are sometimes even harder to tame than
               | C++
               | 
               | Lol what? Unsafe is literally the only "mode" in C++. I'm
               | not trying to shill Rust, but this is not true, and I
               | don't know where you've read that opinion. Unsafe Rust is
               | just Rust with the ability to use "C++ style" pointers.
               | 
               | > The happiness people get from Rust was achieved by the
               | incredibly hard work of library developers
               | 
               | I'll give you that writing Rust libraries is harder, but
               | that's true for any language. Even C++. Do Rust
               | developers go the extra mile to make libraries ergonomic
               | and have nice APIs? Yes! But that's not really a
               | "problem" with Rust, it's something Rust enables, and now
               | the community expects. C++ libraries in my experience
               | tend to be a very bare minimum, and to use some of them
               | it requires hundreds of lines of set up code. In cases
               | where Rust libs require that much set up code, the
               | authors go the extra mile to create macros or additional
               | succinct APIs. So, yeah, their job is harder because they
               | are held to a higher standard than C++ libraries, if
               | anything.
               | 
               | Also, most of the effort with authoring Rust libraries is
               | writing documentation, which is practically a
               | requirement, whereas with most C++ libs (even some
               | sections of boost!) you practically weep tears of joy
               | when you see five lines written above a one class in a
               | sea of classes.
               | 
               | > who had to deal with all sorts of complex semantics
               | under the hood to make safe Rust safe. The knowledge gap
               | between library user and library writer is even worse
               | than C++, where most people do not feel comfortable
               | enough to actually write low-level libraries (custom data
               | structures, bindings from C, optimized routines) in Rust.
               | 
               | Sorry to be so blunt and call you out, but this is just
               | so devoid of any sensible thought.
               | 
               | Most people don't feel comfortable writing low level
               | libraries. Period. Regardless of language. For every
               | library author, there's dozens of not hundreds of non-
               | libray developers. Hell, most developers don't even
               | contribute anything to open source in general, but just
               | consume it.
               | 
               | So pretending C++ is this magical fairytale land where
               | everyone produces libraries with the flick of a wrist is
               | just bullshit.
        
             | boppo1 wrote:
             | Which Stoustrup, principles or the c++ language tome?
        
         | forgotpwd16 wrote:
         | >There are hundreds of ways to implement and optimize similar
         | functionality that I feel like an artist writing in it.
         | 
         | Perl? Can even write poetry in it.
         | https://docstore.mik.ua/orelly/perl/prog3/ch27_02.htm
        
           | avg_dev wrote:
           | that is amazing
        
           | ncmncm wrote:
           | "If new true friend not protected for explicit private union,
           | break case and try using this."
        
             | bstamour wrote:
             | ... I think the perl code actually compiles (err, runs)
             | though. :-)
        
         | medo-bear wrote:
         | > As my first language, I've yet to experience another language
         | that genuinely lets me be as creative as C++
         | 
         | common lisp is cpp's way more talented brother that got
         | addicted to lsd and became a hippy
        
         | latenightcoding wrote:
         | don't worry Perl will let you be 100 times more creative
        
         | mattlondon wrote:
         | Try modern pure JavaScript. It's like floating through air. An
         | absolute joy to code in.
         | 
         | Of course if you want to talk about performance and stuff then
         | JavaScript is not comparable, but that is not the point
        
           | layer8 wrote:
           | The lack of static typing will be a non-starter for many.
        
       | arberx wrote:
       | The "official" reference if anyone is interested:
       | https://en.cppreference.com/w/
        
         | anta40 wrote:
         | And for those who want offline copy:
         | https://en.cppreference.com/w/Cppreference:Archives
        
         | ncmncm wrote:
         | "Official", quote, alludes to very active curation by
         | participants in the standardization process, with example code
         | frequently updated to reflect changing best practice.
         | 
         | Recognized best practice changes with both experience and with
         | newly implemented language features. New features are proposed,
         | and some standardized, specifically because they enable better
         | ways of coding. So, best practice may change markedly with each
         | new Standard.
         | 
         | Generally, _the newest way to do something is usually best_ ,
         | for exactly that reason. (A common misconception has one
         | sticking with older language features for better code. The
         | _reductio ad absurdum_ of this would prefer the C subset.) This
         | does not oblige you to use every feature in every program;
         | e.g., _std::shared_ptr_ has sharply limited value. And, the
         | absolutely newest features may not be very portable yet.
         | 
         | Sometimes more field experience with a feature and its
         | interaction with others leads to changes to best practice. This
         | should be expected particularly in use of concepts and
         | asynchronous facilities.
         | 
         | As an example of change, the present universal preference for
         | "std::" vs "using" took a long time to gel because writers (of
         | articles, slides, textbooks) begrudged the extra space it took
         | on their page.
         | 
         | Examples found on cppreference may usually be relied upon to
         | produce sound results and to steer well clear of both risky
         | usage and bad performance.
        
         | planede wrote:
         | Definitely the best C++ reference. It's not official in the
         | sense that it is not the authoritative standard text itself,
         | but it is very precise and complete.
        
         | qsort wrote:
         | Definitely THE website to bookmark if you're interested in C++
         | at all.
         | 
         | Notice that due to SEO fuckery, your first search result might
         | be cplusplus.com, which is not the same and not as good.
         | cppreference.com is where it's at.
        
       | jjtheblunt wrote:
       | I must be a Scheme "snot", because the definition of "lambda" in
       | C++ (and other languages) is not semantically what lambda in
       | Scheme is.
       | 
       | (There's a different definition of closure than in Scheme, with
       | different implications as a result.)
        
         | ncmncm wrote:
         | C++ lambda is particularly adapted to the needs of C++, and C++
         | programmers. It would be very surprising if it matched any
         | Lisp's. Lisps differ from one another, too.
        
       | stall84 wrote:
       | this is great
        
       | hemloc_io wrote:
       | Is there something like this for Java?
       | 
       | I feel like the more popular languages get the more people write
       | blogspam that gets in the way.
       | 
       | E.g. If you search "libs for rust" you get https://lib.rs/ but
       | try doing "libs for java" and uh, it's not great.
        
         | saghm wrote:
         | I think that probably has more to do with the fact that Rust
         | libraries basically all come from the same place, since there's
         | been centralized package management since the very early days.
         | It's a lot harder to support something like that after the fact
         | than designing for it in advance; new languages like Rust have
         | an advantage in that regard.
        
       | [deleted]
        
       | planede wrote:
       | This looks like a nice start for a good guide. Some hierarchy for
       | the examples would be nice, instead of an alphabetically sorted
       | list. There are some minor inaccuracies here and there, but
       | overall looks alright.
        
       | ComplexSystems wrote:
       | I'm surprised this comments section isn't made up of people
       | talking about how we should never use C++ again and only use Rust
       | from now on.
        
         | ncmncm wrote:
         | That only happens in specially marked Rust-advocacy posts.
         | 
         | More people pick up C++ to use professionally in any given week
         | than the total population of working Rust coders. Rust is
         | practically invisible outside hipster media.
        
           | tialaramex wrote:
           | > More people pick up C++ to use professionally in any given
           | week than the total population of working Rust coders
           | 
           | Nathan here also believes that despite this, firms including
           | Mozilla, Google and Microsoft haven't been able to hire any
           | _engineers_ who can write C++ properly and so that 's the
           | root cause of their problems.
           | 
           | I think that if so very many people "pick up C++ to use
           | professionally" in each week and yet apparently none of them
           | are any good at it, we should consider that the problem might
           | not be with the people at all.
        
             | ncmncm wrote:
             | [flagged]
             | 
             | Do not put words in my mouth. Do not make shit up. Go away.
        
       | agomez314 wrote:
       | this page reminds me of https://learnxinyminutes.com/
        
         | MatthiasPortzel wrote:
         | Learn X in Y minutes in my go-to when I'm writing code in a new
         | language. Just the examples of syntax of a language are very
         | useful and surprisingly hard to come by in formal
         | documentation.
        
       ___________________________________________________________________
       (page generated 2022-09-22 23:02 UTC)