Empirically Measuring, & Reducing, C++βs Accidental Complexity - Herb Sutter - CppCon 2020
Video Statistics and Information
Channel: CppCon
Views: 42,342
Rating: undefined out of 5
Keywords: c++ talk, c++ talk video, cpp talk, cpp talk video, c++, cpp, cppcon, c++con, cpp con, c++ con, cppcon 2020, c++ tutorial, c++ workshop, programming, coding, software, software development, teaching c++, passing parameters in c++, initialization c++, initialization cpp, abstraction c++, abstraction cpp, making cpp easier, making c++ easier, herb sutter, herb sutter c++, herb sutter cppcon, herb sutter 2020, herb sutter youtube, future of c++, C++ is complex, simplifying c++
Id: 6lurOCdaj0Y
Channel Id: undefined
Length: 81min 23sec (4883 seconds)
Published: Sat Oct 10 2020
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.
Hope this proposal doesn't get stuck in the process for 5 years because it seems to really simplify the language. It also makes it safer by enforcing additional compile time checks which are possible only when the intent of passing is known.
Yeess!! I want this.
As someone who only tinkers with C++ now and then, coming back to the language and having to remember all the crazy incantations for references, r-value references etc twists your melon. I am never completely confident on what I am doing.
I have got nothing against ampersands but something like this would be soo much better:
Whats also of interest in Herbs code examples is that he got rid of the const identifiers as well!
...of course, by the time this makes into into the language I will probably be retired!
Okay. That seems like a really good idea, and I have discussed similar things with my colleagues for quite some time. We can see similar approach in other languages, too.
Now, the problem I have with it is, that for in/move/forward parameters, it really IS important, if you are dealing with a reference or a value. Cause if it is a reference, the value underneath can be unexpectedly changed by almost any action.
Let's take the simple example, push_backing into a vector.
With the new syntax, I believe the method signature will be:
constexpr void push_back( in T value );
Now, even though it's not visible, we CAN be dealing with references. Let's consider a common problem: if we need to relocate the vector, and the passed element is part of the vector, we need to take care of that, and push the last element first, cause if we move old elements into new buffer first, we end up copying a moved-from object.The problem with marking parameters "in" that I see, is that in templated code (or if the types can change the way they are passed), our code can work for small trivial types, but break for anything more complex. With "in" parameter we need to always worry about the same thing as with references, and the code can work differently depending on provided type.
I see it as a problematic inconsistency, and a pesimisation of sort, cause if we'd be to completely remove the references, there would be no way to specialize a function, on whether the parameter was passed by value.
These are some thoughts after looking for a way to simplify passing arguments myself. I don't think these problems were mentioned in the presentation.
I don't know how valid these points are - maybe I'm missing something. Still, it feels like it takes away a lot of control over what is going on within our program, and it can bring any kind of surprises.
In the Q&A Herb said that we can completely get rid of references with this. And for parameter passing, sure, maybe. But what about reference members? What about references in ranged for loops? There's at least a dozen other ways to use references. Or is there something I missed?
A few thoughts.
First, I wish he had explained
move
andforward
better in the talk; the paper does a much better job justifying why they exist.Second,
out
parameters and errors don't go very well together; you need exceptions, including presumably Herbceptions (a paper I'm surprised he didn't mention as being related). Because what happens if an error happens and you can't fill the output parameter? Now you've violated the rule that it must be initialized. Even if you're fine with exceptions, not every place that an error appears is a good fit to report that error by an exception, so in such a case are you just boned without
? From a semi-related question during the Q&A it sounds like you'd just have to resort toinout
, but that seems like a major loss. It'd be nice if there was a mechanism for somehow designating the return value of the function as an indication as to whether the variable was initialized. For example, suppose that for a function returning anstd::expected
thatout
meant "if the return value has a value, then the parameter is guaranteed to be initialized, otherwise that guarantee is not met". You'd probably want some kind of customization point so it's not just a single type, and it'd be good to handle the case where there's not a return value other than the error code itself.Third, there was a Q&A about virtual functions, and Herb said that they'd have to match. But I see no reason they couldn't be made contra/covariant (as appropriate for what is being annotated) -- so if a superclass has
foo(inout string s)
then subclasses should be able to restrict that tofoo(out string s)
or maybefoo(in string s)
. (I think -- thinking about whether those relationships are actually properly variant is making my brain hurt. The second of those, overriding aninout
with justin
, would need a different calling convention than justin
to match the superclass.)Fourth, I really wish both talk and paper had more actual real examples of code instead of mostly just
f
s.Fifth, the paper mentions an optional rule (inspired by, e.g., C#) of marking call sites with at least
out
orinout
to match, so your call would bestd::vector<int> v = uninitialized; foo(out v);
. I really really really hope this is allowed or even required. ("Allowed" presumably would mean "would be possible to require for a project via clang-tidy rules or whatever", so that's fine.) I could stop arguing for my semi-unpopular opinion that one should pass out parameters by pointer instead of reference so that there's an indication at call sites that something passed as a parameter can be modified. (Edit: This point I added later, but I could have sworn I had there before... I guess I deleted it?)This is the best C++ proposal I've seen in a while. This will make C++ even friendlier than Rust.
One thing I believe C++ gets right and other languages get wrong is object identity. An object of some type
T
is uniquely identified by its address. This is apparent when passing function arguments.In my opinion, the fact that other languages (Java, Python) do not have this distinction is a mistake. It makes programs more difficult to reason about, not less.
For me, the proper way to pass arguments to functions is eminently clear - either by value, or by reference with the correct set of permissions.
What do the
in
,out
, andinout
qualifiers gain that me that is worth the cost of giving up control over object identity?I am heavily invested into high performance C++ and so I am always afraid that new C++ standards might take away some old fundamental functionality (like pointers or pointer arithmetic) which would cause a lot of work for me and maybe even make some performance optimisations impossible.
However the changes proposed in this talk would be welcome in my projects: I had to implement a nasty pod_vector this year because
std::vector
does not support uninitialised memory allocations. Also thein
,out
,inout
, ... parameters wouldn't cause too much work for me. Generally I am fine with deprecating old C++ (especially from the STL) if this does not deteriorate performance.So I had a quick play with the compiler extension and I did manage to crash it:
The good news is that it's crashing on a pathological example, and that makes me happy. I'm curious to see if such an aliasing fault could come about when using the parameter attributes consistently.