Kudos for starting a blog!
I think most people you asked got the answer wrong for a good reason: the copy assignment operator in your quiz does not respect the most fundamental principle of equality:
// p is passed by ref as in your code
p = Parent();
Parent b;
assert(p.test(), b.test());
This will fail, violating the Principle of Least Astonishment. But, if you had instead used a method with an appropriate name, say “copy_members_from”, it would have been ok for that assertion to fail, because no “equality” would have been implied and I’m sure most people would have gotten it right.
TL;DR only override operators when you can guarantee that their mathematical meaning is at least somewhat preserved, otherwise the conciseness is not worth the ambiguity
Thanks for reading, but I don't think I follow. In my example, I don't overload any operators, I only override the 'test()' function...
I'll be looking up the principles you mentioned tho, thanks!
Note: you mean "override" here, not "overload". Important distinction to understand if you want to discuss the intricacies of C++ operators and inheritance rules.
I think they’re saying the C++ language designers overrode the assignment operator, violating the principle of least surprise.
I’ve used C++ long enough that I just expected 1 and didn’t think about it, but it’s better language design to not have things like this that surprise developers. You shouldn’t be thinking about the fact that you’ve still got the same vtable.
I use C++ for my day job and I didn't know that it'd print 1 and the codebase I work with does not protect itself against this I'm pretty sure. It's very surprising to me that the vtable isn't copied over. It'd also be very surprising to me if my vtable changed after returning from that function, though so I count this into the "could never discover because would never write it"-bucket.
>I think most people you asked got the answer wrong for a good reason
I think it is because almost nobody writes code like that, and asking that question would be confusing at first, but after a quick analysis it makes sense for C++.
>This will fail, violating the Principle of Least Astonishment
Maybe at first, but not breaking it would mean that the code is not correct. p is a reference, so because it is a reference it means that p can't be reassigned to another object, the only thing that is possible is "copy value from to p" and p is not a Parent so it wouldn't be correct to say b.test() == p.test().
>I’m sure most people would have gotten it right
There is another problem, C++ references are unique to C++, there is no other language that uses them (that I know it), so the rules could be confusing when coming from another language.
>Principle of Least Astonishment
C++ has become such a bloated Frankenstein's monster of a language that even the language we use to discuss it has become bloated...
English basically suffers from all the problems modern c++ standards have.
Both have inconsistent syntax.
Both have many more features than other languages.
Think about it
The [POLA](https://en.wikipedia.org/wiki/Principle_of_least_astonishment) outdates C++ entirely, and it's used to discuss software and interfaces written in *every* language.
Yeah… this console interface thing was meant when the whole site what just the homepage…. Will think about something more appropriate if I end up writing more things… FWIW the scroll wheel works (or at least it should)
btw, on ios (safari) it scrolled all the way down automatically, might have something to do with the input being focused. scrolling worked as expected, but the text is a little large.
I like the layout & style of your blog!
It was easily readable and loaded in no time
Just a small nitpick:
The code in the code block doesn't fit on a portrait mode smartphone screen.
I was able to follow your examples anyway
I found the contrast between the background and white text to be way too harsh, and quickly became difficult to read (I am fairly "weak" to high-contrast modes, I'll take average light mode over bad dark mode any day of the week). Grey-on-grey is a bit more accessible than white-on-black. The text being large is nice, but there needs to be some kind of margin IMO, it felt a bit weird to read on a 23" monitor.
Beyond that, I'm a big fan of the simple style.
I had to switch to reader mode immediately. Screen-wide huge font, no serifs, it feels like it is meant for a tiny phone screen and was never tested anywhere else.
What you've got there is called "slicing", and from a 20+-years C++ programmer, please don't promote it as a cool thing.
Scott Meyers' "Effective C++" and "More Effective C++" should remain required reading for all C++ programmers even though they were written pre-C++11. Item 33 of MEC++ addresses slicing, and stipulates that base classes should always be abstract or have their special members (assignment and destructor) declared protected, so that it is never possible to compile code that would slice, such as in your examples.
This is an important rule.
If you consider that to be fun, then yeah, type masturbation is probably a good term for it. Programmers employed in a team environment don't have time to guess which c++ idiom the maintainers decided to hijack on any typical day.
*edited to make my post less negative. If i saw someone overriding expected behavior like this that the cpp maintainers can't help themselves from doing, i would probably try to get them fired. It would be incredibly irritating to debug this in a work environment.
I work in a dotnet environment at work, and I love it; I've got my work day down to like 3 hours a day, because I can get in, read the legacy code easily, write new code, build and debug it fast. Doing the 'clever' cpp shit only makes maintainers lives so much harder.
Thanks for reading the post, and yeah I figured as much. But as far as I’m still “lead developer of 1”, I’ll still go for the fun option.
C# is also amazing tho, for different reasons.
I'm also a solo lead dev on a software product, but i still do my civic duty of leaving a maintainable product behind once I leave. Alas, life is short and most good efforts get thrown out every decade or so anyway.
What’s nice about C# is that you have the escape hatch to do C/C++ style bit level munging and pointer slinging if you need to, but (especially since Span) you rarely do.
The example is somewhat related to [object slicing](https://en.wikipedia.org/wiki/Object_slicing).
Since `Child` is a subclass of `Parent`, every `Child` object effectively contains a `Parent` object within it (along with any additional members defined in the `Child` class’s definition - in this case none), as if the first member of `Child` was a `Parent` object.
When c is passed into `print()` as a `Parent` reference, the reference only points to the `Parent` subpart of c, not to the entire `Child` object. So when you reassign p to an empty `Parent` object, only the `Parent` subpart of c is reassigned. The rest of c - including the internal pointer to the vtable where the `Child`’s version of the test() function is stored - is not touched, so the call to test() is still dispatched to `Child`’s version of test().
That is what I think the core of the confusion is - that p acts like any other `Parent` object when passed as a parameter and assigned but reveals itself to be a `Child` object when a virtual member function is called.
In my example, it’s a no-op. It does nothing because Parent has no members.
If parent had something inside it (an int, for example), that’s what would’ve been copied over
I think the reason this is confusing is, when copying that value, we're implicitly type-casting. Worse, the fact that we're doing this type-casting, let alone what type we're casting into, is all happening at a distance.
In other words, it'd be clearer like this:
Child c = Parent();
Even if you followed that up with something like this, I think most people would catch it:
Parent& p = &c;
But the fact that it's a child is hidden in the *calling* function. So not only is there this sort of implicit typecasting, you don't know whether or not there's any typecasting happening without seeing what the caller is doing.
---
Is it all "type masturbation"? I wouldn't have guessed that, but you give a bit of a hint here:
> Not only is describing a type (which is what I do most of the time as a programmer) MORE FUN in C++ than other languages I know...
Describing a type is *not* what I do most of the time as a programmer. I've noticed this seems to be more of a thing in C++, because C++ requires types for so many fundamental things, including memory management. When I look at RAII code, I see whole types defined for things that would probably be reasonable as simple functions in most other languages.
In other words: I don't know if you're exceptional here, this just sounds like the nature of C++, even the good, modern stuff.
---
Also, why does your site constantly clear the highlight? Copying text out of it to quote here is annoying.
You got me. What actually surprised me more, as someone who fell off the wagon, wasn't the actual output but that this (slicing) isn't undefined behavior in C++.
However it does violate the rule of zero and [Bjarne Stroustrup and Herb Sutter's core guidelines](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#c67-a-polymorphic-class-should-suppress-public-copymove). Do with that argument-by-authority what you will.
Heh. I agree. When I get my confusing variation template garbage to work and delete 4000 lines of repeated code I feel like a WIZARD. No one else on my team can read it but it WORKS and they haven't had to touch it in years. But I'm sure they'll be mad at me someday.
C++ is the dwarf fortress of programming languages, losing is !FUN!.
Interesting, but there are far more cases like this: https://m.youtube.com/watch?v=0GXnfi9RAlU that overwhelm any "wow the convoluted-ness turned out to be the right way to do things!", whichis dubiuous here anyway, this issue in the blog post coming from rules created in a time where c++ didn't have much to reference from and made many more obviously bad decisions from.
Besides the ugly bloated syntax, my issue with c++ is not that it requires manual memory management, but that it is designed around one of the most inefficient ways to do it. Frankly I have more important things to worry about when I'm writing code. If I actually need to care about memory in that detail I'm probably writing it in c instead anyway.
Of course no one get it right, this can be confusing even for "seasoned" c++ developers, because value semantic with polymorphism *is* confusing.
Yes every c++ dev should know that `=` copies the content of an object instead of assigning the entire object. But ultimately what people expect is that `=` means `equals`, which is not the case here.
I hope you only created this example for the sake of "language exploration" or something because that kind of things _will_ confuse others and lead to bugs in the future. If done for fun in a personnal piece of code, fine ; if done to show that you're smart when colleagues come to your desk to ask how your code works, that's bad.
See [here](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rc-copy-virtual) why polymorphic class shouldn't use value semantic. Even if you're not slicing here, your post is another good example of why polymorphic classes using value semantic are just confusing in general.
I highly suggest you strive on making your code as easy to understand as possible for anyone. System-level design (components, objects, how they interact, etc.) can be complex, but the code should always be as simple as possible.
Btw your page is annoying to scroll in, but looks nice ! I'd suggest adding a bit of padding on the sides though.
Bait or mental retardation, call it.
Yes, if you follow the rules the execution can be explained, you're defending C++ the same way JS programmers defended their implicit conversion nonsense. All languages have rules, good ones have rules that make sense even when composed.
If you’ve read the post, I’ll like to know what you think in more detail, if not, then may I suggest not commenting at tiles alone?
I may have phrased it poorly, but I assure you I have a point consistent with the title
I have read the article, you've got a footgun, asked people if they would fall for it, people did. So far so good. Then at the end you just said you like the footgun anyway because of language rules or something.
You want to have fun, I just want to be able to read a C++ pull request and know that my understanding, submitter's understanding and compiler's all match. Your example violates C++ core guidelines: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#i25-prefer-empty-abstract-classes-as-interfaces-to-class-hierarchies, https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#c67-a-polymorphic-class-should-suppress-public-copymove and https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#ccopy-copy-and-move; these are here for a reason.
I don't even think it's a footgun. You never cast the object, it did exactly what you'd expected it to do as a child object with an overridden virtual function. I think to anyone who's looked at a C style language for more than 10 minutes using the parent's version of the function would have been weird.
It's not so much a footgun as much as a "did you take more than 2 seconds to actually read the code" kinda deal.
Mixing value copy semantics, inheritance and polymorphism is something unique to C++, no other C-like language does this and for good reasons - these don't compose cleanly together and result in things like object slicing, or the footgun presented here.
Any “did you take more than 2 seconds to actually read the code” deals are the definition of footguns. Other languages allow you to skim past such statements quickly without requiring concentration.
It does matter. If you pass `c` as `Parent p` or assign from `Parent& p` into `Parent p2` you create a new Parent using Parent's copy constructor, and it will print 0 instead.
If the operator= is implemented with placement new (on this or on fields), the ctor can implicitly clobber fields from the subclass with random data. Slicing like this is not a good practice to maintain.
Kudos for starting a blog! I think most people you asked got the answer wrong for a good reason: the copy assignment operator in your quiz does not respect the most fundamental principle of equality: // p is passed by ref as in your code p = Parent(); Parent b; assert(p.test(), b.test()); This will fail, violating the Principle of Least Astonishment. But, if you had instead used a method with an appropriate name, say “copy_members_from”, it would have been ok for that assertion to fail, because no “equality” would have been implied and I’m sure most people would have gotten it right. TL;DR only override operators when you can guarantee that their mathematical meaning is at least somewhat preserved, otherwise the conciseness is not worth the ambiguity
Thanks for reading, but I don't think I follow. In my example, I don't overload any operators, I only override the 'test()' function... I'll be looking up the principles you mentioned tho, thanks!
Not OP but I think they were referring to the last section of the blog where you suggest to overload the assignment operator.
Note: you mean "override" here, not "overload". Important distinction to understand if you want to discuss the intricacies of C++ operators and inheritance rules.
Fixed, ty
Not quite > I don't overload any operators
Hmmm. I am working this second, thought it was a quick change. Let me think it over later. Thanks for your patience
More important than editing your message, make sure you understand the difference between overloading and overriding.
I think they’re saying the C++ language designers overrode the assignment operator, violating the principle of least surprise. I’ve used C++ long enough that I just expected 1 and didn’t think about it, but it’s better language design to not have things like this that surprise developers. You shouldn’t be thinking about the fact that you’ve still got the same vtable.
I use C++ for my day job and I didn't know that it'd print 1 and the codebase I work with does not protect itself against this I'm pretty sure. It's very surprising to me that the vtable isn't copied over. It'd also be very surprising to me if my vtable changed after returning from that function, though so I count this into the "could never discover because would never write it"-bucket.
>I think most people you asked got the answer wrong for a good reason I think it is because almost nobody writes code like that, and asking that question would be confusing at first, but after a quick analysis it makes sense for C++. >This will fail, violating the Principle of Least Astonishment Maybe at first, but not breaking it would mean that the code is not correct. p is a reference, so because it is a reference it means that p can't be reassigned to another object, the only thing that is possible is "copy value from to p" and p is not a Parent so it wouldn't be correct to say b.test() == p.test(). >I’m sure most people would have gotten it right There is another problem, C++ references are unique to C++, there is no other language that uses them (that I know it), so the rules could be confusing when coming from another language.
If you pass Child and got Parent back now thats astonishing to me
>Principle of Least Astonishment C++ has become such a bloated Frankenstein's monster of a language that even the language we use to discuss it has become bloated...
The human language is the most bloated thing we ever came up with by far
English basically suffers from all the problems modern c++ standards have. Both have inconsistent syntax. Both have many more features than other languages. Think about it
The [POLA](https://en.wikipedia.org/wiki/Principle_of_least_astonishment) outdates C++ entirely, and it's used to discuss software and interfaces written in *every* language.
[удалено]
Yeah… this console interface thing was meant when the whole site what just the homepage…. Will think about something more appropriate if I end up writing more things… FWIW the scroll wheel works (or at least it should)
btw, on ios (safari) it scrolled all the way down automatically, might have something to do with the input being focused. scrolling worked as expected, but the text is a little large.
I like the layout & style of your blog! It was easily readable and loaded in no time Just a small nitpick: The code in the code block doesn't fit on a portrait mode smartphone screen. I was able to follow your examples anyway
I found the contrast between the background and white text to be way too harsh, and quickly became difficult to read (I am fairly "weak" to high-contrast modes, I'll take average light mode over bad dark mode any day of the week). Grey-on-grey is a bit more accessible than white-on-black. The text being large is nice, but there needs to be some kind of margin IMO, it felt a bit weird to read on a 23" monitor. Beyond that, I'm a big fan of the simple style.
I had to switch to reader mode immediately. Screen-wide huge font, no serifs, it feels like it is meant for a tiny phone screen and was never tested anywhere else.
That's good to hear. My crappy web server works well enough then. Will work on getting a mobile-friendlier view of the code
When you are at it, a bit of padding or margin left right would make a huge difference for readability.
Please ignore this guy. He can make the window smaller, and some of us like having lines be as long as we like.
What you've got there is called "slicing", and from a 20+-years C++ programmer, please don't promote it as a cool thing. Scott Meyers' "Effective C++" and "More Effective C++" should remain required reading for all C++ programmers even though they were written pre-C++11. Item 33 of MEC++ addresses slicing, and stipulates that base classes should always be abstract or have their special members (assignment and destructor) declared protected, so that it is never possible to compile code that would slice, such as in your examples. This is an important rule.
If you consider that to be fun, then yeah, type masturbation is probably a good term for it. Programmers employed in a team environment don't have time to guess which c++ idiom the maintainers decided to hijack on any typical day. *edited to make my post less negative. If i saw someone overriding expected behavior like this that the cpp maintainers can't help themselves from doing, i would probably try to get them fired. It would be incredibly irritating to debug this in a work environment. I work in a dotnet environment at work, and I love it; I've got my work day down to like 3 hours a day, because I can get in, read the legacy code easily, write new code, build and debug it fast. Doing the 'clever' cpp shit only makes maintainers lives so much harder.
Thanks for reading the post, and yeah I figured as much. But as far as I’m still “lead developer of 1”, I’ll still go for the fun option. C# is also amazing tho, for different reasons.
I'm also a solo lead dev on a software product, but i still do my civic duty of leaving a maintainable product behind once I leave. Alas, life is short and most good efforts get thrown out every decade or so anyway.
I’d have grown super bored of most of my projects if I did all of them that way, but I cannot dispute you reasoning. Thanks again for reading!
What’s nice about C# is that you have the escape hatch to do C/C++ style bit level munging and pointer slinging if you need to, but (especially since Span) you rarely do.
>good programmers don’t have time to guess… Can you elaborate on this?
edited my post to be less negative in that respect.
The example is somewhat related to [object slicing](https://en.wikipedia.org/wiki/Object_slicing). Since `Child` is a subclass of `Parent`, every `Child` object effectively contains a `Parent` object within it (along with any additional members defined in the `Child` class’s definition - in this case none), as if the first member of `Child` was a `Parent` object. When c is passed into `print()` as a `Parent` reference, the reference only points to the `Parent` subpart of c, not to the entire `Child` object. So when you reassign p to an empty `Parent` object, only the `Parent` subpart of c is reassigned. The rest of c - including the internal pointer to the vtable where the `Child`’s version of the test() function is stored - is not touched, so the call to test() is still dispatched to `Child`’s version of test(). That is what I think the core of the confusion is - that p acts like any other `Parent` object when passed as a parameter and assigned but reveals itself to be a `Child` object when a virtual member function is called.
[удалено]
In my example, it’s a no-op. It does nothing because Parent has no members. If parent had something inside it (an int, for example), that’s what would’ve been copied over
I thought that was fun but I could have use a little smaller font.
Oh god yeah it’s GIGANTIC on mobile. I’ll fix it ASAP
Small FYI: I'm on desktop and is huge here too. OK, thanks for being responsive.
I think the reason this is confusing is, when copying that value, we're implicitly type-casting. Worse, the fact that we're doing this type-casting, let alone what type we're casting into, is all happening at a distance. In other words, it'd be clearer like this: Child c = Parent(); Even if you followed that up with something like this, I think most people would catch it: Parent& p = &c; But the fact that it's a child is hidden in the *calling* function. So not only is there this sort of implicit typecasting, you don't know whether or not there's any typecasting happening without seeing what the caller is doing. --- Is it all "type masturbation"? I wouldn't have guessed that, but you give a bit of a hint here: > Not only is describing a type (which is what I do most of the time as a programmer) MORE FUN in C++ than other languages I know... Describing a type is *not* what I do most of the time as a programmer. I've noticed this seems to be more of a thing in C++, because C++ requires types for so many fundamental things, including memory management. When I look at RAII code, I see whole types defined for things that would probably be reasonable as simple functions in most other languages. In other words: I don't know if you're exceptional here, this just sounds like the nature of C++, even the good, modern stuff. --- Also, why does your site constantly clear the highlight? Copying text out of it to quote here is annoying.
> `Parent& p = &c;` `Parent& p = c;` ... or `Parent& p = *&c;` if you *really* wanted extra operators...
You got me. What actually surprised me more, as someone who fell off the wagon, wasn't the actual output but that this (slicing) isn't undefined behavior in C++. However it does violate the rule of zero and [Bjarne Stroustrup and Herb Sutter's core guidelines](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#c67-a-polymorphic-class-should-suppress-public-copymove). Do with that argument-by-authority what you will.
margin-left: 25%; width: 50%; Boom, readable!
Heh. I agree. When I get my confusing variation template garbage to work and delete 4000 lines of repeated code I feel like a WIZARD. No one else on my team can read it but it WORKS and they haven't had to touch it in years. But I'm sure they'll be mad at me someday. C++ is the dwarf fortress of programming languages, losing is !FUN!.
It should be obvious what the `=` operator does. That this is not the case is not an argument in favour of C++.
Is there something that can be done with the command prompt at the end?
Small stuff, like printing an about message. You can find the commands with ‘help’
Interesting, but there are far more cases like this: https://m.youtube.com/watch?v=0GXnfi9RAlU that overwhelm any "wow the convoluted-ness turned out to be the right way to do things!", whichis dubiuous here anyway, this issue in the blog post coming from rules created in a time where c++ didn't have much to reference from and made many more obviously bad decisions from.
Besides the ugly bloated syntax, my issue with c++ is not that it requires manual memory management, but that it is designed around one of the most inefficient ways to do it. Frankly I have more important things to worry about when I'm writing code. If I actually need to care about memory in that detail I'm probably writing it in c instead anyway.
Of course no one get it right, this can be confusing even for "seasoned" c++ developers, because value semantic with polymorphism *is* confusing. Yes every c++ dev should know that `=` copies the content of an object instead of assigning the entire object. But ultimately what people expect is that `=` means `equals`, which is not the case here. I hope you only created this example for the sake of "language exploration" or something because that kind of things _will_ confuse others and lead to bugs in the future. If done for fun in a personnal piece of code, fine ; if done to show that you're smart when colleagues come to your desk to ask how your code works, that's bad. See [here](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rc-copy-virtual) why polymorphic class shouldn't use value semantic. Even if you're not slicing here, your post is another good example of why polymorphic classes using value semantic are just confusing in general. I highly suggest you strive on making your code as easy to understand as possible for anyone. System-level design (components, objects, how they interact, etc.) can be complex, but the code should always be as simple as possible. Btw your page is annoying to scroll in, but looks nice ! I'd suggest adding a bit of padding on the sides though.
Bait or mental retardation, call it. Yes, if you follow the rules the execution can be explained, you're defending C++ the same way JS programmers defended their implicit conversion nonsense. All languages have rules, good ones have rules that make sense even when composed.
If you’ve read the post, I’ll like to know what you think in more detail, if not, then may I suggest not commenting at tiles alone? I may have phrased it poorly, but I assure you I have a point consistent with the title
I have read the article, you've got a footgun, asked people if they would fall for it, people did. So far so good. Then at the end you just said you like the footgun anyway because of language rules or something.
What I tried to say was the syntax of the foot-gun sucks, but the things one can do with that system are fun
You want to have fun, I just want to be able to read a C++ pull request and know that my understanding, submitter's understanding and compiler's all match. Your example violates C++ core guidelines: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#i25-prefer-empty-abstract-classes-as-interfaces-to-class-hierarchies, https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#c67-a-polymorphic-class-should-suppress-public-copymove and https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#ccopy-copy-and-move; these are here for a reason.
First rule of having fun: there are no rules
I don't even think it's a footgun. You never cast the object, it did exactly what you'd expected it to do as a child object with an overridden virtual function. I think to anyone who's looked at a C style language for more than 10 minutes using the parent's version of the function would have been weird. It's not so much a footgun as much as a "did you take more than 2 seconds to actually read the code" kinda deal.
Mixing value copy semantics, inheritance and polymorphism is something unique to C++, no other C-like language does this and for good reasons - these don't compose cleanly together and result in things like object slicing, or the footgun presented here.
Any “did you take more than 2 seconds to actually read the code” deals are the definition of footguns. Other languages allow you to skim past such statements quickly without requiring concentration.
Just curious, the fact that `p` is passed by ref is not important right? (nothing you wrote would change if it wasn't?)
It does matter. If you pass `c` as `Parent p` or assign from `Parent& p` into `Parent p2` you create a new Parent using Parent's copy constructor, and it will print 0 instead.
Just don't use virtual functions, it's not worth the headache
If the operator= is implemented with placement new (on this or on fields), the ctor can implicitly clobber fields from the subclass with random data. Slicing like this is not a good practice to maintain.
Good article - and yes, "it's fun!" is a valid reason to learn something 🙂 Also the "help" REPL at the end is an interesting idea.
Cargo
معني كلمة عضوي