Thank you so much for the re-upload!! I was so interested in this talk but it was hard to watch with the slides being out of sync. Can’t wait to dive in!
Update: great talk. Jon hits on a friction with move semantics and weakening invariants that has bothered me for a long time, and his arguments about the requirements on moved-from objects are well-articulated. I have to think destructive move (relocate) would basically fix things here. If a moved-from object simply no longer exists, we don’t have to spend any time reasoning about or maintaining its state, either as class authors or class users.
@@_noisecode Thanks for your kind words. I think the issue with what you are suggesting is that we often want to move from objects to which we want to (later) assign a new value. This is important for swapping, sorting, and any number of valuable algorithms. Consider an element in a container. If moving from that object makes the object no longer exist, what does this mean for the container. It now holds an element that no longer exists, but the container has no way of knowing that (because we can move from objects without notifying that container that holds them).
As someone who makes a living teaching C++ - there goes the conflict of interest. You can have performance-heavy options while making safe the default. But you can't acknowledge that because it's literally a challenge to your bottom line which depends on C++ being more complicated than it needs to be.
@CuriousCauliflowerX As an individual that makes my living teaching C++, I have never once had a thought C++, might not be complicated enough for my “bottom line.” Nor has it ever occurred to me that I might make C++ so easy to understand that I could put myself out of business.
Amazing lecture / talk. Honestly i had no idea that these were design choices. Maybe i should blame the university lecturers, or myself. Always seemed like the language was simply unfinished.
He's basically listing many problems, that Rust solves. I.e., accessing a moved object, is not possible in Rust at compile time. Overflow, underflow and division by zero, is checked in Debug mode, but not in Release mode.
Congratulations!! Very interesting exposition about the burden introduced with move semantics on RAII and lifetime of objects, years ago when studying this I realized the same problems, and I concur it is a pity this had been implemented this way. Let's see if for cpp2 Herb fixes this problem, since function constructors are a great oportunity to consider moving to destruct the object and fix once and for always this problem.
Thanks for the talks but I don't agree some parts. 1. compilers are optimized out ub not because they can but because the standards allows. So that means that compiler does know what is ub or not. So either compiler can generate error for that or the as-if rule should be updated so that the compilers cannot optimize out the ub. In ideal world, ub doesn't happen but it is not an ideal world. There will be ub code. to optimize out these parts can create hidden bugs which is causes a lot of issue. 2. UB is not a some hidden power, it is actually weaknesses of the C++. Especially not so senior developers. For the ideal language, they may be unspecified behaviour which can be changed from environment to environment but there shouldn't be any undefined behaviour. There are couple of reasons for that. First, if all programmers will do the same thing to prevent the ub, then this ub should have some defined variables. Think about uninitialized variable cases. if a variable is not initialize -> it can be optimized out -> so there should not be any uninitialized variable -> so all programmers must initialize these variables. If all programmers somewhat initialize any variables, then it is logical to add zero initialization. Second, UB sometimes create hidden bugs. I understand that there should be some unit test, etc but again, we are not an ideal world. And as you mentioned in memory safe language, "the believe that the use of memory safe language create buggy application and kills the people". This argument is also valid for UB. so why we have UB then? One possible answer is that the program should have unit test/static analyzer to prevent ub. then the same argument is also applied in memory safe language. then why cpp doesn't have the memory safe feature? Did you get it?
@muzikleringucu Thanks for your comment. You asked, Did I get it? But I’m wondering this about your. This presentation goes to great lengths to explain why C++ has Undefined Behavior, but your comment indicates that your didn’t get that. I do want to address your first paragraph. You said, “So that means that compiler does know what is ub or not.” It is true that the compiler writer must understand what is or is not Undefined Behavior, but it doesn’t follow that the compiler “compiler can generate error for that.” Writing code that assumes there is no Undefined Behavior is very different than detecting and reporting every instance of Undefined Behavior. Let me give you one simple example. Image that one translation unit has a function that take a pointer and dereferences it. It doesn’t check the pointer value for nil first, for performance reasons. But without knowing every call to that function (which it can’t know because calls to the function might be in other translation units) the compiler has to assume that this may be undefined behavior.
8:08 “the index operator is unsafe because of performance […] in a loop you don’t need bounds checks”. But shouldn‘t one use iterators instead? then you’d have `vec[i]` being checked and slow, `vec.get_unchecked(i)` being verbose, fast, and unsafe, and iterators being fast, elegant, and safe.
@flying-sheep Thanks for you comment. Whether or not you use iterators or index values is orthogonal to the issue. Just like index values, iterators can be checked or unchecked and by default, like operator[], they are unchecked. If you are arguing that the operator[] (the default?) version should be checked and the uncheck version should be explicitly asked for, that ship has sailed, but if that had the decision, it would be that the concise version (operator[]) would be hardly every used because C++ programs would almost always chose the fast version.
Nondestructive move semantics chosen by C++ are not a performance improvement - they're a performance degradation that introduces unnecessary branching in the destructor code and redundant execution of destructor code because it has to execute for moved from objects even when there's nothing to destruct. It clearly violates the "performance above else" principle, rust's move semantics are more performant. It's very funny that C++ programmers became so deranged as to find themselves doing reassuring chants. "This is C++!". C++ doesn't have to care about safety. Programmers don't have to care about C++. You're free to stay in your bubble, just don't expect everybody else to also be a luddite. Just don't cry when your language can't be used anymore for safety-critical industries and applications. No more mars rovers in C++.
@CuriousCauliflowerX Thanks for your comment. I can’t recall a single instance where I’ve seen a destructor branch added due to move semantics. I can’t know, but I suspect your understanding or assumptions about nondestructive move semantics are uniformed by real world code.
Did it ever occur to anyone that automatically setting uninitialised variables to zero is no better than leaving as some random number or declaring that using it is UB? Compilers can detect this error at compile time. Why doesn't C++? For this reason, and many others, I will not be using C++ anymore. Because this is C++. By the way, I have never heard anyone who advocates using memory safe languages confusing memory safety with overall logic correctness of programs. They are not stupid. They welcome using static analysis as well. Static analysis is able to work far better when it can use the information provided by the source it is given, information that is missing from C++ source.
In C++26, compilers can detect it and warn you. Why didn't it? Because undefined behavior _in general_ is not something the compiler has to issue a diagnostic for. It was a case of "one size fits all". The compiler might detect it but rather than warning you, optimize the code out because this path (that produces undefined behavior) should never be taken. But seriously, why are you declaring your variables before they are ready to receive their initial value? Don't cluster than at the top -- put them at first use.
There's always a speed vs safety trade off in any language. If you want bounds checking you must pay for it in CPU cycles. IMO, safety is more important than speed in most cases.
The amount of logical falacies used was ... beyond measurement. Java has been with us for 29 years and C# - 22 years. But, obviously, for C++ to have a chance, we must all forget about these completely unknown languages and ecosystems. Also, I winder, does the speaker uses safety belts in his car? They are proven to not work above some speeds, while preventing almost all injuries bellow t0 km/h. By his logic, they are completely useless because ... That's the C++ way. Also, the tooling outside of the C++ ecosystem is way way way way way better. Also, we know that we cannot know what "a+b" actually works. With the preprocessor, operator overloads, and sufficiently advanced template magic, "a+b" can launch a LLM, which generates a command in ancient Egyptian to a REST api of fhe USA military force fo launch a nuclear strike. Or, you know, add a and b. And of course, let's not forget that C++ is completely undeadable, and significant parts of a c++ program is not busy expressing business logic but placating the compiler or working around language deficiencies. std::move, anyone? In what sane language this is needed? And this unreadability hides bugs. And prevents creating actually working tools, such as the ones the memory safe languages have been enjoying for decades. And last but not least, the C++ compiler has been doing math and all sorts of magic calculations since forever, the idea that "it only cares about types" is blatantly false. But, again, unless we outright lie, we cannot weasel ourself out of the increadible badness of that cursed language.
> std::move, anyone ? In what sane language this is needed Most languages have reference semantics, value semantics (and so move semantics) are rare. But std::move does give a performance boost for value semantic languages in some situations.
@todortotev5399 Thanks for your comment. If your performance goals allow for a managed language like Java or C++, then they are likely good candidates for your application.
I hate it when moments like 7:20 happen, is it really necessary to stop the host to make a comment just to show off you knowledge? C'mon this is high school behaviour..
This is absurd, seeing UB as the language’s super power is crazy when it is the thing that will ultimately destroy the language and cede the ground to Rust and other memory safe languages. The problem with C++ isn’t performance above safety, the problem is that there are no safe options easily available. For example, in many cases numerical overflow may not be a concern, but when it is, a huge burden is put on the developer to deal with it. Putting code in something like of ‘safe’ and ‘unsafe’ blocks allows the programmer to clearly indicate what they want and when they are willing to take the performance hit, this still sits with the zero overhead philosophy. Adopting easily opted-in/opted-out safety is as important as performance to the future of the language.
@brunobignose Thanks for your comment. I disagree that it is a huge burden to use a safe int library in situations where that is appropriate. The point of C++ is have a no-compromise-on-performance option when that is what is needed.
I cannot agree that the requirement for moved-from object to be in a valid state is a mistake. Otherwise the standard must define a new state of object. Also, moving operation can be conditional, so there must be a uniform way to detect that state. All this would bring more complexity to the core language, not just the standard library.
@romanzeleny1982 Thanks for taking the time to watch the video. I don’t agree, for all the reasons that I put in the video. Note that although it would be helpful for the standard to spell out what I advocate for in the video, it isn’t truly necessary. As long as the standard doesn’t get in the way, which seems to be its direction. It is true that moving may be conditional, but there is no situation where that is true, that proper (non-logic error) code doesn’t just assume that the object is, in fact, moved from. This doesn’t add any complexity. The complexity is already there. The only question is do we write code that neither compromises performance nor contains logic errors.
Thank you for reuploading the video with the slide timings fixed!
I found this talk to be quite insightful.
So very pleased to hear that you found this talk to be informative. Thank you for your comment which is much appreciated!
Thanks for your kind words.
That was a very good talk!
Thanks for your kind words.
The talk is much better with the synchronized slides. Thank you C++Now for uploading this fixed version of the video.
Thank you!
Thank you so much for the re-upload!! I was so interested in this talk but it was hard to watch with the slides being out of sync. Can’t wait to dive in!
Update: great talk. Jon hits on a friction with move semantics and weakening invariants that has bothered me for a long time, and his arguments about the requirements on moved-from objects are well-articulated. I have to think destructive move (relocate) would basically fix things here. If a moved-from object simply no longer exists, we don’t have to spend any time reasoning about or maintaining its state, either as class authors or class users.
Yes, the out of sync version was a difficult watch! Thank you so much for your appreciation of the re-upload!
@@_noisecode Thanks for your kind words.
I think the issue with what you are suggesting is that we often want to move from objects to which we want to (later) assign a new value. This is important for swapping, sorting, and any number of valuable algorithms.
Consider an element in a container. If moving from that object makes the object no longer exist, what does this mean for the container. It now holds an element that no longer exists, but the container has no way of knowing that (because we can move from objects without notifying that container that holds them).
As someone who makes a living teaching C++ - there goes the conflict of interest. You can have performance-heavy options while making safe the default. But you can't acknowledge that because it's literally a challenge to your bottom line which depends on C++ being more complicated than it needs to be.
@CuriousCauliflowerX As an individual that makes my living teaching C++, I have never once had a thought C++, might not be complicated enough for my “bottom line.” Nor has it ever occurred to me that I might make C++ so easy to understand that I could put myself out of business.
Amazing lecture / talk.
Honestly i had no idea that these were design choices.
Maybe i should blame the university lecturers, or myself.
Always seemed like the language was simply unfinished.
Thanks for your kind words.
No language which is in widespread industry use is ever finished.
1:14:18 Safety keynote from C++Now 2023: ua-cam.com/video/Gh79wcGJdTg/v-deo.html
Safety panel from C++Now 2023: ua-cam.com/video/jFi5cILjbA4/v-deo.html
Thanks for the re-upload. Poor alignment of slides to speech boundaries can be a big hit on communication efficiency.
Thanks to you and all others who commented on the problem with the original version of Jon Kalb's presentation.
He's basically listing many problems, that Rust solves. I.e., accessing a moved object, is not possible in Rust at compile time. Overflow, underflow and division by zero, is checked in Debug mode, but not in Release mode.
Very very interesting !
@JudgeFredd Thanks for your kind words.
Congratulations!!
Very interesting exposition about the burden introduced with move semantics on RAII and lifetime of objects, years ago when studying this I realized the same problems, and I concur it is a pity this had been implemented this way.
Let's see if for cpp2 Herb fixes this problem, since function constructors are a great oportunity to consider moving to destruct the object and fix once and for always this problem.
Thanks for your kind words. We’ll have to see where cpp2 ends up.
This didn't age well - google just demonstrated you can REMOVE some UB and add bounds checking to container accesses and incur hardly any penalty.
@rinket7779 Thanks for your comment. The focus of my talk is on problem domains where “hardly any penalty” isn’t acceptable.
Thanks for the talks but I don't agree some parts.
1. compilers are optimized out ub not because they can but because the standards allows. So that means that compiler does know what is ub or not. So either compiler can generate error for that or the as-if rule should be updated so that the compilers cannot optimize out the ub. In ideal world, ub doesn't happen but it is not an ideal world. There will be ub code. to optimize out these parts can create hidden bugs which is causes a lot of issue.
2. UB is not a some hidden power, it is actually weaknesses of the C++. Especially not so senior developers. For the ideal language, they may be unspecified behaviour which can be changed from environment to environment but there shouldn't be any undefined behaviour. There are couple of reasons for that.
First, if all programmers will do the same thing to prevent the ub, then this ub should have some defined variables. Think about uninitialized variable cases.
if a variable is not initialize -> it can be optimized out -> so there should not be any uninitialized variable -> so all programmers must initialize these variables.
If all programmers somewhat initialize any variables, then it is logical to add zero initialization.
Second, UB sometimes create hidden bugs. I understand that there should be some unit test, etc but again, we are not an ideal world.
And as you mentioned in memory safe language, "the believe that the use of memory safe language create buggy application and kills the people".
This argument is also valid for UB. so why we have UB then? One possible answer is that the program should have unit test/static analyzer to prevent ub. then the same argument is also applied in memory safe language. then why cpp doesn't have the memory safe feature? Did you get it?
@muzikleringucu Thanks for your comment. You asked, Did I get it? But I’m wondering this about your. This presentation goes to great lengths to explain why C++ has Undefined Behavior, but your comment indicates that your didn’t get that.
I do want to address your first paragraph. You said, “So that means that compiler does know what is ub or not.” It is true that the compiler writer must understand what is or is not Undefined Behavior, but it doesn’t follow that the compiler “compiler can generate error for that.” Writing code that assumes there is no Undefined Behavior is very different than detecting and reporting every instance of Undefined Behavior.
Let me give you one simple example. Image that one translation unit has a function that take a pointer and dereferences it. It doesn’t check the pointer value for nil first, for performance reasons. But without knowing every call to that function (which it can’t know because calls to the function might be in other translation units) the compiler has to assume that this may be undefined behavior.
8:08 “the index operator is unsafe because of performance […] in a loop you don’t need bounds checks”. But shouldn‘t one use iterators instead?
then you’d have `vec[i]` being checked and slow, `vec.get_unchecked(i)` being verbose, fast, and unsafe, and iterators being fast, elegant, and safe.
@flying-sheep Thanks for you comment. Whether or not you use iterators or index values is orthogonal to the issue. Just like index values, iterators can be checked or unchecked and by default, like operator[], they are unchecked.
If you are arguing that the operator[] (the default?) version should be checked and the uncheck version should be explicitly asked for, that ship has sailed, but if that had the decision, it would be that the concise version (operator[]) would be hardly every used because C++ programs would almost always chose the fast version.
Nondestructive move semantics chosen by C++ are not a performance improvement - they're a performance degradation that introduces unnecessary branching in the destructor code and redundant execution of destructor code because it has to execute for moved from objects even when there's nothing to destruct. It clearly violates the "performance above else" principle, rust's move semantics are more performant.
It's very funny that C++ programmers became so deranged as to find themselves doing reassuring chants. "This is C++!".
C++ doesn't have to care about safety. Programmers don't have to care about C++. You're free to stay in your bubble, just don't expect everybody else to also be a luddite. Just don't cry when your language can't be used anymore for safety-critical industries and applications. No more mars rovers in C++.
Nah, they use tightly-regulated C for mars rovers
@CuriousCauliflowerX Thanks for your comment. I can’t recall a single instance where I’ve seen a destructor branch added due to move semantics. I can’t know, but I suspect your understanding or assumptions about nondestructive move semantics are uniformed by real world code.
Did it ever occur to anyone that automatically setting uninitialised variables to zero is no better than leaving as some random number or declaring that using it is UB? Compilers can detect this error at compile time. Why doesn't C++? For this reason, and many others, I will not be using C++ anymore. Because this is C++.
By the way, I have never heard anyone who advocates using memory safe languages confusing memory safety with overall logic correctness of programs. They are not stupid. They welcome using static analysis as well. Static analysis is able to work far better when it can use the information provided by the source it is given, information that is missing from C++ source.
In C++26, compilers can detect it and warn you.
Why didn't it? Because undefined behavior _in general_ is not something the compiler has to issue a diagnostic for. It was a case of "one size fits all". The compiler might detect it but rather than warning you, optimize the code out because this path (that produces undefined behavior) should never be taken.
But seriously, why are you declaring your variables before they are ready to receive their initial value? Don't cluster than at the top -- put them at first use.
@@JohnDlugosz Thanks for the comment. In particular advocating for the best practice of declaring variables at first use.
There's always a speed vs safety trade off in any language. If you want bounds checking you must pay for it in CPU cycles. IMO, safety is more important than speed in most cases.
The amount of logical falacies used was ... beyond measurement.
Java has been with us for 29 years and C# - 22 years. But, obviously, for C++ to have a chance, we must all forget about these completely unknown languages and ecosystems.
Also, I winder, does the speaker uses safety belts in his car? They are proven to not work above some speeds, while preventing almost all injuries bellow t0 km/h. By his logic, they are completely useless because ... That's the C++ way.
Also, the tooling outside of the C++ ecosystem is way way way way way better.
Also, we know that we cannot know what "a+b" actually works. With the preprocessor, operator overloads, and sufficiently advanced template magic, "a+b" can launch a LLM, which generates a command in ancient Egyptian to a REST api of fhe USA military force fo launch a nuclear strike. Or, you know, add a and b.
And of course, let's not forget that C++ is completely undeadable, and significant parts of a c++ program is not busy expressing business logic but placating the compiler or working around language deficiencies. std::move, anyone? In what sane language this is needed? And this unreadability hides bugs. And prevents creating actually working tools, such as the ones the memory safe languages have been enjoying for decades.
And last but not least, the C++ compiler has been doing math and all sorts of magic calculations since forever, the idea that "it only cares about types" is blatantly false. But, again, unless we outright lie, we cannot weasel ourself out of the increadible badness of that cursed language.
> std::move, anyone ? In what sane language this is needed
Most languages have reference semantics, value semantics (and so move semantics) are rare. But std::move does give a performance boost for value semantic languages in some situations.
@todortotev5399 Thanks for your comment. If your performance goals allow for a managed language like Java or C++, then they are likely good candidates for your application.
Isn’t it reupload but with corrected slides?
Yes. Because the original upload was unwatchable.
I hate it when moments like 7:20 happen, is it really necessary to stop the host to make a comment just to show off you knowledge? C'mon this is high school behaviour..
1:01:02 clang tidy already warns against using moved-from objects. Even standard containers I think.
@AlfredoCorrea Nice to know.
This is absurd, seeing UB as the language’s super power is crazy when it is the thing that will ultimately destroy the language and cede the ground to Rust and other memory safe languages.
The problem with C++ isn’t performance above safety, the problem is that there are no safe options easily available. For example, in many cases numerical overflow may not be a concern, but when it is, a huge burden is put on the developer to deal with it. Putting code in something like of ‘safe’ and ‘unsafe’ blocks allows the programmer to clearly indicate what they want and when they are willing to take the performance hit, this still sits with the zero overhead philosophy. Adopting easily opted-in/opted-out safety is as important as performance to the future of the language.
@brunobignose Thanks for your comment. I disagree that it is a huge burden to use a safe int library in situations where that is appropriate. The point of C++ is have a no-compromise-on-performance option when that is what is needed.
I cannot agree that the requirement for moved-from object to be in a valid state is a mistake. Otherwise the standard must define a new state of object. Also, moving operation can be conditional, so there must be a uniform way to detect that state. All this would bring more complexity to the core language, not just the standard library.
@romanzeleny1982 Thanks for taking the time to watch the video. I don’t agree, for all the reasons that I put in the video.
Note that although it would be helpful for the standard to spell out what I advocate for in the video, it isn’t truly necessary. As long as the standard doesn’t get in the way, which seems to be its direction.
It is true that moving may be conditional, but there is no situation where that is true, that proper (non-logic error) code doesn’t just assume that the object is, in fact, moved from.
This doesn’t add any complexity. The complexity is already there. The only question is do we write code that neither compromises performance nor contains logic errors.