[HN Gopher] My favourite C++ footgun
___________________________________________________________________
My favourite C++ footgun
Author : pabs3
Score : 99 points
Date : 2021-06-21 09:29 UTC (1 days ago)
(HTM) web link (dustri.org)
(TXT) w3m dump (dustri.org)
| ot wrote:
| In modern C++ the use of `new` and `delete` should be considered
| smell, for this and other reasons.
| fooker wrote:
| That's not a valid evaluation order, afaik. You can not start
| evaluating one argument, stop and switch to the other, then come
| back to the earlier one.
| MaxBarraclough wrote:
| Do take a look at dvt's comment. Prior to C++17, it was a valid
| evaluation order.
| bregma wrote:
| Prior to C++17 such an evaluation order would have violated
| the standard. ISO/IEC 14882:2011 1.9/13 [Note:
| Indeterminately sequenced evaluations cannot overlap, but
| either could be executed first. -- end note]. The arguments
| in a faction call are a comma-separated indeterminate
| sequence (5.2.2/4 [Note: Such initializations are
| indeterminately sequenced with respect to each other (1.9) --
| end note]).
| MaxBarraclough wrote:
| > Prior to C++17 such an evaluation order would have
| violated the standard
|
| Herb Sutter disagrees. [0] The C++ legalese you've quoted
| seems decisive, unfortunately Sutter's post doesn't mention
| it.
|
| See also [1][2].
|
| _edit_ quietbritishjim beat me to it, and with a better
| informed comment to boot.
|
| [0] https://herbsutter.com/2013/05/29/gotw-89-solution-
| smart-poi... (ctrl-f for _known_ , and see also the comment
| thread at the bottom of the page)
|
| [1] https://stackoverflow.com/a/48844115/
|
| [2] https://stackoverflow.com/a/46472497/
| ectopod wrote:
| Is there any version of C++ in which this interleaving is
| permitted?
| quietbritishjim wrote:
| The full context to that second quote is:
|
| > When a function is called, each parameter ([dcl.fct])
| shall be initialized ([dcl.init], [class.copy],
| [class.ctor]) with its corresponding argument. [ Note: Such
| initializations are indeterminately sequenced with respect
| to each other ([intro.execution]) -- end note ]
|
| So it's only about initialisation of parameters from
| arguments, not to the evaluation of arguments. (For example
| if foo() takes a std::string parameter, and bar() returns a
| const char*, then in the expression foo(bar()) the above
| quote refers to the call to the string constructor, _not_
| to the call to bar()).
|
| The more relevant part is 5.2.2/8:
|
| > [ Note: The evaluations of the postfix expression and of
| the argument expressions are all unsequenced relative to
| one another. All side effects of argument expression
| evaluations are sequenced before the function is entered
| (see [intro.execution]). -- end note ]
|
| As others have said, this didn't change until C++17.
| eklitzke wrote:
| The author writes "this might be why there is std::make_shared",
| but that's not really why it exists. When you create a
| std::shared_ptr it needs to allocate memory for a control block
| structure (which tracks the reference count) as well as the
| memory for the object itself. If you write
| std::shared_ptr<Foo>(new Foo) this will result in two memory
| allocations, one for the Foo object and the other for the
| std::shared_ptr control block. If you write
| std::make_shared<Foo>() then a single allocation will happen
| which has enough space for both the control block and the Foo
| object, and then placement new is used to initialize the memory
| for both structures. So std::make_shared exists to reduce the
| number of memory allocations that are being made, not for the
| reason suggested here.
| quietbritishjim wrote:
| It exists for both reasons.
|
| That's why there's also a std::make_unique even though it
| doesn't have a control block (although it was added later, but
| that was just an oversight).
| MaxBarraclough wrote:
| > It exists for both reasons.
|
| I believe this is correct. Here's [0] some old Boost
| documentation on Boost's _make_shared_ which inspired the C++
| standard 's _make_shared_. It mentions both reasons.
|
| [0] https://www.boost.org/doc/libs/1_67_0/libs/smart_ptr/doc/
| htm...
| haldean wrote:
| There's a crazy downside to make_shared that I learned recently
| because of this: if you have a weak pointer to a shared thing,
| and the refcount for the shared thing drops to zero, the weak
| pointers will keep the allocation for the object "alive",
| because they still need access to the remnant and the remnant
| was created in the same allocation as the object so they can't
| be freed separately. So now I only use make_shared if I know
| for sure there won't be a weak_ptr pointing at it (or if the
| base object has a relatively small memory footprint after it's
| been destructed).
| ot wrote:
| Yeah, that's definitely something to be aware of. It's
| usually not an issue as most objects have small footprint
| (and any allocations they in turn hold would be released when
| the _strong_ refcount goes to 0).
| asveikau wrote:
| I think the idea that you are also relying on a presumably well
| tested library to get the exception corner cases right is also
| noteworthy. Memory leaks on allocation failure are pretty
| common in naive code, and a good thing to handle in a library
| where it can get well thought out.
| Koshkin wrote:
| This is very important. Try to hide most of complexity in a
| library, and then unit-test the hell out of it.
| CraigJPerry wrote:
| A capturing lambda is another way to leak a std::shared_ptr<> - i
| feel like it's an even easier way to run into this kind of
| footgun but i suppose it depends whether you typically use lots
| of lambdas.
|
| A reasonable solution in my case could be to use std::weak_ptr<>
| instead but that wouldn't be useful here.
| quietbritishjim wrote:
| > A capturing lambda is another way to leak a std::shared_ptr<>
|
| How so? Do you mean if you do exactly what the article says
| when initialising the capture variables? Or something lambda
| specific?
| overgard wrote:
| My (least) favorite footgun is "auto" when it comes to
| references.
|
| If you want to get a pointer from something, you would write
| this: auto fooPtr = mywidget.getPtr();
| fooPtr->doCoolStuff();
|
| But we all know references are better than pointers right? So we
| should just write... auto fooRef =
| myWidget.getRef(); fooRef.doCoolStuff()
|
| The problem is... this is valid and will probably not do what you
| want. It will make a copy. What you want is actually
| auto& fooRef = myWidget.getRef();
|
| I once spent an entire day debugging a bizarre crash because of
| that. I was asking for a reference to a scene graph, and what was
| actually happening is I was getting a clone of the scene graph
| (which was an object that couldn't be safely copied), and then
| destroying a bunch of shared pointers when the function returned.
| Fun times.
| criddell wrote:
| If you develop on Windows and aren't using Visual Studio 2019.
|
| "auto fooRef = myWidget.getRef()" will get a little squiggle
| under it to warn you that you are making a copy.
| overgard wrote:
| I am very happy tooling is helping to make these sorts of
| mixups easier to find, although sadly C++ is such a hard
| language to write tooling for that those kinds of nice
| features are pretty rare. I remember back when I had to work
| in Xcode a few years ago I could rarely even get basic
| intellisense to work.
| Koshkin wrote:
| > _and aren 't using Visual Studio_
|
| OK, I tried this in Notepad, and it did not work.
| malkia wrote:
| yeah, same with WordPad, but see Word would give you some
| squiggles :)
| amluto wrote:
| > What you want is actually > > auto& fooRef =
| myWidget.getRef();
|
| That variant has issues if getRef() actually returns a
| temporary object. You may want auto&&.
| bregma wrote:
| Wait, you had an object that was unsafe to copy, but you told
| the computer it was safe by not deleting the copy constructor?
| I guess it should have done what you wanted, not what you told
| it.
| contravariant wrote:
| Yeah I learned that the hard way when I found out that
| std::vector _apparently_ feels free to move your objects
| around in memory for you, no matter if you point to them from
| somewhere else.
|
| I mean in hindsight it's obvious, but still not exactly what
| I wanted to happen.
|
| For reference, what I did does actually work, temporarily.
| daniel-levin wrote:
| I think if you come from the JVM / CLR world, you are so
| protected by the runtime's patching up of references that
| it might not even occur to you that a (raw) pointer to a
| data structure's internals can dangle after the data is
| moved around. The runtimes mentioned pause your code, move
| things around and even compact the heap and your references
| magically still point to what they did before!
| tines wrote:
| > Yeah I learned that the hard way when I found out that
| std::vector apparently feels free to move your objects
| around in memory for you
|
| In standard terminology, this is described as "invalidating
| iterators". There are a bunch of member functions in
| std::vector that either do or don't invalidate iterators,
| e.g. push_back(...) does but size() doesn't. And as the
| name implies, if you call a function that invalidates
| iterators, all your existing iterators/pointers/references
| become invalid.
| Kranar wrote:
| Iterator invalidation is different from this. Iterator
| invalidation, as the name suggests, only applies to
| iterators. The problem OP was having had to do with
| dangling references.
|
| Some containers guarantee references won't dangle on
| mutation such as unordered_map. An unordered_map may
| invalidate iterators if objects are added or removed, but
| will never result in dangling references (unless the
| object is removed). That is, it is safe to have a pointer
| to an object owned by an unordered_map and continue using
| that pointer even after an iterator to that same object
| is invalidated.
| tines wrote:
| Ah, interesting. Even within that dichotomy, I'd think
| that if references aren't invalidated on mutation, then
| dereferencing iterators would be valid, but incrementing
| them would not be.
| einpoklum wrote:
| Iterators are historically a generalization of pointers.
| A pointer is a kind of iterator.
|
| I mean, literally. The iterator of an std::vector<T> (not
| a bool vector) is a T*.
| _huayra_ wrote:
| This is why things like std::list still have value:
| iterator stability. You even get this in most map/sets too.
|
| But vector is usually what one should reach for unless one
| has a good reason not to. The only danger really comes when
| the lifetime of iterators and the thing they point to
| become a bit decoupled, even due to insertion!
| einpoklum wrote:
| You can also use an offset instead of a pointer (or a
| pointer to the object and and an offset into its heap
| storage, or whatever).
| Dylan16807 wrote:
| > told the computer it was safe
|
| > by _not_ deleting
|
| I think there's a problem here.
| overgard wrote:
| Well, the scene graph code wasn't code I wrote, and this was
| back in 2014. But sure, obviously it was an error on my
| part... we are talking about footguns afterall, I'm the one
| that pulled the trigger.
| secondcoming wrote:
| No, _everyone_ gets LHS auto wrong at the start. They don 't
| realise it makes a copy. I have to point it out every other
| code review.
| jcelerier wrote:
| The rule is simple: auto behaves like a template parameter
| Kranar wrote:
| There is nothing simple about that (not to mention it's
| not actually true either).
| Koshkin wrote:
| Ah, templates... Universal references... Good stuff.
| Kranar wrote:
| Yep, except now we're supposed to call them forwarding
| references because the motivation Scott Meyers had for
| naming them universal references turned out to be
| incorrect in subtle ways.
|
| Even the notable experts get things wrong when it comes
| to how complex and bloated C++ is.
| suprfsat wrote:
| Simply by using C++, you tell the computer all sorts of
| things, such as "I know the entire language by heart", and "I
| have a death wish".
| owl57 wrote:
| While C++ is a dangerous tool indeed, ignoring the rule of
| 5 is analogous to ignoring the safety guidelines, not
| simply using the tool.
| yongjik wrote:
| A C++ object can be perfectly safe to copy in one
| situation and conjure up nasal demons in another
| situation. I don't think rule of 5 would help in that
| case.
| decker wrote:
| Sounds like the root cause was due to not obeying the rule of
| five:
| https://en.wikipedia.org/wiki/Rule_of_three_(C%2B%2B_program...
| overgard wrote:
| Probably! I don't remember the code exactly, this was back in
| 2014 and I think most of the code I had to consume was
| primarily written by a 24 year old out of college (smart of
| course, but C++ takes a long time to get good at).
| einpoklum wrote:
| So, if I write:
|
| int x; auto y = x;
|
| do you expect y to be a reference? Surely not.
|
| No, my friend, auto is for values, not references. You want a
| reference - you have to say so.
|
| (And same goes for rvalue references - auto&& .)
| leetcrew wrote:
| I too have fallen victim to this and similar traps. it's a good
| idea to use a deleted copy constructor when you know it's not
| safe to copy the object. you can't completely stop others (or
| yourself) from doing bad things, but you can at least give them
| a moment to reflect on what they are doing.
| nyanpasu64 wrote:
| I wish C++ copy constructors were explicit by default.
| secondcoming wrote:
| It's not about safety, it's about efficiency. It's usually an
| unintended copy. For example, if your function `foo` returns
| a `const std::string&` the code `auto x = foo()` creates a
| copy.
| jchw wrote:
| As others have pointed out, the idiomatic solution here is to
| delete the copy constructor if an instance is unsafe to copy --
| however, I suspect the _reason_ why auto behaves differently
| from other inference in requiring this to be explicit is
| probably something along the lines of making it harder to have
| a dangling reference. It's shockingly easy to wind up with a
| dangling reference with fairly innocuous code, something like
| QString().toUtf8().data() so maybe this makes sense. (Doesn't
| help for that case since it's a pointer to raw data, but you
| get the picture.)
| nyanpasu64 wrote:
| Worse yet is an object which _is_ safe to copy in some cases,
| but you intended to not copy it at the moment, and you create
| a dangling reference to a field within. I wish C++ copy
| constructors were explicit by default. (But declaring the
| copy constructor explicit turns off aggregate initialization,
| so I can 't do that.)
| overgard wrote:
| I think, all things considered, the way it works is probably
| the best way, since you might want to use "auto" to actually
| make a copy. It's just very surprising, because when I have
| explicit types I'm used to looking for that sort of error,
| but with an auto I wasn't (at the time) used to looking for
| that.
| dvt wrote:
| The nested function execution interleaving blew my mind (imagine
| having to debug that), so I had to look it up. Apparently,
| interleaving is prohibited in C++17 onward. So this:
|
| > The second one is there since this is C++, priority() might
| raise an exception, meaning that new Widget will be called, but
| never passed to std::shared_ptr, and thus never deleted!
|
| Is now impossible (thank God!). See a full SO discussion here[1].
| Stuff like this makes me so happy I don't write C++ anymore, but
| the gist of it is (from the standard):
|
| > For each function invocation F, for every evaluation A that
| occurs within F and every evaluation B that does not occur within
| F but is evaluated on the same thread and as part of the same
| signal handler (if any), either A is sequenced before B or B is
| sequenced before A.
|
| [1] https://stackoverflow.com/questions/38501587/what-are-the-
| ev...
| ncmncm wrote:
| In three decades' use of C++, of all generations going back to
| original cfront, I have never encountered any difficulty,
| confusion, or actual problem arising from this phenomenon. It
| is a favorite of language lawyers and people borrowing trouble.
| TwoBit wrote:
| > Is now impossible
|
| I don't see how that is so. C++ 17 allows new Widget to
| complete and then the priority() call to execute and throw
| before both are passed to shared_ptr(), thus creating a leak.
| Your cited example [1] doesn't leak because both arguments are
| shared_ptr. Seems to me that C++ 17 does indeed solve this
| latter case but not the former.
| nyanpasu64 wrote:
| > both are passed to shared_ptr()
|
| priority() is _not_ passed to shared_ptr(), only to
| processWidget().
| dvt wrote:
| Per [1]:
|
| > evaluations of A and B are _indeterminately sequenced_ :
| they may be performed in any order but may not overlap:
| either A will be complete before B, or B will be complete
| before A. The order may be the opposite the next time the
| same expression is evaluated.
|
| And:
|
| > 21) Every expression in a comma-separated list of
| expressions in a parenthesized initializer is evaluated as if
| for a function call ( _indeterminately-sequenced_ )
|
| Emphasis mine. What the above basically says is that in the
| case of some function `f(A, B)`, the arguments `A`, and `B`,
| are what's known as "indeterminately-sequenced" -- this mean
| that their execution cannot be interleaved (overlap) -- but
| they still individually execute in a non-deterministic order
| (A before B, and sometimes B before A)!
|
| With that said, the good news is that B can now never throw
| in the middle of A, which is precisely what we have in OP's
| example.
|
| [1] https://en.cppreference.com/w/cpp/language/eval_order
| _huayra_ wrote:
| Whew thanks for looking this up! I was afraid I'd have to
| add yet another entry to my gigantic C++ footguns.org
| document. It's getting so big now that Emacs struggles a
| bit to load it due to inline code examples!
| shric wrote:
| At first I thought that was a domain and not an org file.
| :)
|
| Looks like footguns.com is taken but footguns.org is
| still available.
| dale_glass wrote:
| My favorite: slicing.
|
| That one blew my mind when I found out. You can rip off a chunk
| of an object by accident... and nothing happens whatsoever.
| There's no warning from the compiler.
|
| You can stuff a Derived into a std::list<Base>, it'll work just
| fine, and if Derived happened to say, hold a pointer, it'll
| happily vanish into the ether.
|
| Besides the amount of confusion that can ensure from this, it
| means you can have problems if you want to introduce inheritance
| into a place that didn't have it before.
|
| Especially if it's some sort of external component. You think
| you're clever inheriting from a framework class and adding some
| data on top? Nope, the internal structure you don't control
| doesn't want to cooperate with that plan.
| user-the-name wrote:
| Well, here we have an entire thread of nothing but reasons to
| never write anything in C++.
|
| Jesus christ, what an utter mess of a language.
| MaxBarraclough wrote:
| One of my favourites too. Here's a good StackOverflow answer on
| the topic. [0] Looking here [1] though, did something change in
| C++17 to ameliorate things? _edit_ Turns out yes, see dvt 's
| comment. _Second edit_ Apparently [2] I knew this 3 years ago and
| forgot.
|
| [0] https://stackoverflow.com/a/7693775/
|
| [1] The final bullet-point in the Notes section of
| https://en.cppreference.com/w/cpp/memory/shared_ptr/make_sha...
|
| [2] https://stackoverflow.com/questions/38501587/what-are-the-
| ev...
| 2bitencryption wrote:
| My favorite C++ footgun is creating a Vector with some items,
| taking a reference to, say, &vec[3], then adding another item to
| the vec, then trying to use the reference from the previous step.
|
| If you write C++ it might be obvious what the problem is.
|
| If you don't, this will absolutely ruin your entire day.
|
| The worst part is, 95% of the time, it will probably work without
| issue.
|
| But eventually, pushing a new item to the Vector will trigger a
| relocation of the whole vector, which will invalidate your
| reference and bring down production. Have fun debugging that.
| pizza234 wrote:
| Interestingly, AFAIK also Golang suffers from something
| similar: creating a slice from an array, then performing an
| operation on the array, that causes resizing - the slice will
| keep pointing to the old array data.
| bentcorner wrote:
| I can understand people used to a HLL running into this. It's
| useful to read the documentation:
| https://en.cppreference.com/w/cpp/container/vector/insert
| mentions that references/iterators may be invalidated.
|
| I'm not going to pretend that I understand everything on that
| site (particularly anything about complex templates and things
| like SFINAE) but often there's comprehensible stuff in there.
| It can be really helpful.
| flyingswift wrote:
| What is the safe way to achieve the same result?
| TylerGlaiel wrote:
| store the index 3 as an int instead of &vec[3]
| [deleted]
| Kranar wrote:
| The concept behind this is reference stability, and if you
| need a collection that has stable references, you must
| introduce a level of indirection, that is, instead of a
| vector<T>, you use a vector<unique_ptr<T>> and then you can
| take references as follows: auto& r =
| *some_vector[0];
| flyingswift wrote:
| Thanks! I am just learning C++ for a new gig, and coming
| from Javascript land, it is a lot to take in :)
| yongjik wrote:
| In addition to other answers, sometimes you do know the
| final/max length of the vector when you construct it. In that
| case reserve() can reserve the necessary space, and as long
| as you stay under the limit all the addresses will remain
| valid.
|
| (Though it's still pretty brittle, so you may want to add a
| ton of comments to warn yourself in the future...)
| kaik wrote:
| I kid you not, I spent a full working day debugging this exact
| same issue (taking a pointer to a vector element, before adding
| more elements). Very obvious if you understand how C++ and
| vectors work, yet it took me forever to realize, and it was
| miserable...
| nicoburns wrote:
| Yep. I learnt about this when I was learning Rust (which makes
| this a compile-time error). I was very glad I didn't have to
| learn this and the 100 other things like that seem to exist in
| C++ the hard way!
| krylon wrote:
| You can run into the same problem in C, using malloc/realloc.
| realloc, in fact, makes for a nasty footgun, too (and remains,
| of course, available in C++).
| turminal wrote:
| Yes, but unlike with realloc and a custom dynamic array, C++
| references and smart pointers and containers are half-smart
| and do all sorts of things on their own. Knowing when they
| will and when they will not be "smart" makes writing C++
| really difficult sometimes.
| duped wrote:
| This, and the entire class of iterator invalidation bugs that
| force you to memorize which collections are ok for which
| applications.
| nyanpasu64 wrote:
| Rust takes the approach of flat-out not letting you mutate
| _any_ collection while you have _any_ references to its
| contents. It eliminates all dangling pointer bugs... I don 't
| know if it rules out any useful use cases of collections
| _with_ iterator stability. I think any C++ collection holding
| unique_ptr _is_ stable (pushing to the collection doesn 't
| invalidate the target of the unique_ptr), and Rust doesn't
| have an safe ergonomic way to achieve that (perhaps
| Pin<Box<MagicCell<T>>>, but we don't yet have a MagicCell
| that makes &mut MagicCell<T> not noalias).
| duped wrote:
| The underlying pointer to a unique_ptr won't be invalidated
| but the iterator might. Consider if you had a vector of
| unique_ptrs and inserted into it within a for loop.
| Depending on the implementation of the iterator this may
| not be sound (if it's an index, you're probably ok, if it's
| a pointer, you're screwed).
|
| If you wanted to do the same in Rust it would be
| Vec<Box<T>>. Mutating the collection won't invalidate the
| pointers.
| liquidify wrote:
| Any raw new in code is a smell. I thought this was old news?
| astrange wrote:
| I've noticed that every few years when I look at C++ again,
| everyone says this about whatever was correct last time.
| _huayra_ wrote:
| Jumping on the footgun bandwagon, one I had always known, but
| never realized how bad of a design decision it was was "the c++
| rvalue lifetime disaster" [0]. The talk linked in [0] has a
| different conclusion, but describes the problem well.
|
| Basically, the lifetime aspects of r/l-values are improperly
| coupled to the "can I scavenge the internals?" aspect (via
| const). R-values should not have been promoted to const-ref (I
| think it was a legacy behavior?) because then this const-ref can
| be passed around (e.g. returned) as if it were going to outlive
| the original reference's frame!
|
| This was the first time I realized what a mess C++ can be
| (although I still use it a lot). This is a subtle error that I
| have seen seasoned experts miss in code review (especially in
| templated code). You can't work around it like you can the
| underperforming parts of the stdlib (Abseil and Folly help out a
| lot!). This promotion choice of auto&& -> auto const& is really
| baked into the language and I don't see a path to change it.
|
| The other "biggest foot cannon" has gotta be the incredibly
| subtle ways one can violate the ABI, e.g. executable A linking
| against libs B and C, each of which bundle an ABI-incompatible
| implementation of the same parent dep D (versions 1.1 and 2.1).
| You never know which type of object you're really passing around
| with D::SomeType!
|
| [0] https://quuxplusone.github.io/blog/2020/03/04/rvalue-
| lifetim...
| rileymat2 wrote:
| If you try to use inheritance, object slicing by far.
|
| I understand the practical reasons for it, but it feels very
| wrong.
| ncmncm wrote:
| It only ever comes up in toy examples. Working programmers do
| not encounter it, so in practice it is never the cause of a
| problem.
| yongjik wrote:
| I think it's basically the same problem as unique_ptr? This is
| the best explanation I've seen: https://herbsutter.com/gotw/_102/
| jeffbee wrote:
| This way to call a function smells wrong anyway. A shared pointer
| to a new value is nonsense as a parameter, because at the point
| of the call the pointer is not shared, it is unique! Since a
| shared pointer is implicitly constructible from a unique pointer,
| this makes more sense, assuming there are other call sites that
| really intend to share ownership: void
| frob(std::shared_ptr<T>); main() {
| frob(std::make_unique<T>()); }
| ot wrote:
| If you know that the pointer is going to be shared, it's more
| efficient to use `make_shared` as it will use a single
| allocation for both the object and the control block.
| Koshkin wrote:
| Well, usually you designate a pointer as 'shared' not only when
| it happens to be already shared but also when it will be shared
| later on.
| Kranar wrote:
| You've now forgone an important optimization opportunity for
| absolutely no benefit.
| throwaaskjdfh wrote:
| Why does it seem like C++ is constantly replacing its subtle
| hazards with even more subtle hazards? It's like they never go
| away, they just turn into something more obscure.
| plorkyeran wrote:
| This is an example of a very old subtle hazard that has been
| removed entirely (the ordering which causes problems is no
| longer permitted in C++17).
| [deleted]
| AnimalMuppet wrote:
| In addition to what plorkyeran said, "more obscure" can mean
| either "harder to understand" or "less often encountered". The
| second one is progress.
| pshc wrote:
| It's Stroustrup's Full Employment Theorem in action.
| Kranar wrote:
| Because the people involved with C++ have a toxic aversion to
| learning from other languages. Anytime issues are brought up
| the people involved with C++ standardization keep yapping the
| line that "But C++ is not like <other language>." and then
| proceed to implement a half-assed version of what other
| languages have and then 2-3 years later people find all kinds
| of footguns that could have simply been avoided had proper
| research been performed.
|
| The big issue is that becoming involved in the C++
| standardization process is purposely arcane, requiring people
| to physically travel to remote locations, miss time off work,
| and spend a lot of money. There are literally substantial
| language features added to C++ that are nothing more than the
| work of maybe 3-4 people. These features could have benefited
| enormously from having 100s, if not 1000s of potential
| developers exercising use cases and contributing suggestions,
| but the standardization process is heavily gated.
| duped wrote:
| I feel like this is a strawman.
|
| 9/10 times when there is resistance to a particular feature
| it's because implementing it breaks the ABI.
|
| > requiring people to physically travel to remote locations,
| miss time off work, and spend a lot of money.
|
| It's a different kind of barrier to entry, but I would be
| surprised if the attendees of the standards meetings aren't
| being compensated for it.
| Koshkin wrote:
| Well, no doubt it all comes from good intentions. (With which,
| you know, the road to hell is paved.)
| towergratis wrote:
| The real problem is not specific to C++, memory or shared
| pointers, but as the author mentions later, the fact that
| "function parameters evaluation order is unspecified".
|
| The problem is similar in C as well.
|
| `printf("%d, %d", i++, i++);` will give you different results
| depending on the compiler.
| Koshkin wrote:
| The situation is even worse, due to the possible interleaving
| of the execution of function calls.
| towergratis wrote:
| The root cause is the same
| Koshkin wrote:
| Well, except that C does not have exceptions (at least not
| in the way that C++ does).
___________________________________________________________________
(page generated 2021-06-22 23:00 UTC)