template <typename T>
struct optional {
// One version of value which works for everything
template <class Self>
constexpr auto&& value(this Self&& self) {
if (self.has_value()) {
return std::forward<Self>(self).m_value;
}
throw bad_optional_access();
}
The fact that I can read this and don't even find it that terrible is proof that C++ has permanently damaged my brain.
i've been using c++ since the mid 80s (cfront), and it is my favourite language, but i sometimes have bad thoughts about modern c++ syntax - if double ampersands (logic op excepted) are now with us, can triple ampersands be far behind? i wish we could maybe introduce a few new keywords.
mind you, i feel much the same regarding rust syntax.
Wouldn't C's approach with macros help? E.g. the language defines a keyword like _Foobar which is supposed to be used via #include <foobar> that defines a "#define foobar _Foobar" macro, thus avoiding any collisions with existing code and any new code that needs to use the new keyword can opt-in and deal with any collisions (since the code will be modified anyway).
stroustrup's ambition for c++ was to get rid of the preprocessor (unfufilled, sadly). also, defining a name such as _Foobar does not avoid a clash with existing code - names like that are reserved in c++ and may be used by the standard library.
The standard library is at least under the control of the people implementing the standard, which is much smaller breakage (ie can be fixed at the time of implementing support for the keyword) than breaking a random users code.
The standard could go as far as to say what <foobar> should provide and then leave it up to the implementation what the actual non-aliased keyword actually is: maybe it’s _Foobar or maybe it’s something else, the end user won’t know because you just use foobar after including <foobar>
yeah, i know all about that. but still, i think that c++ (and c, for that matter) could do with more alphabetic keywords. and after all, the original language introduced quite a few!
Triple ampersand already came up somewhere. I don't quite remember in what context but iirc they simplify to normal lvalue refs (at least in that context).
I think you're talking about perfect forwarding and reference collapsing rules [1, 2], introduced in C++11, but that is a bit different thing occurring only in a template instantiation context. Normally, you wouldn't see triple ampersands in sane C++ code, afaik.
One question I have about the double ampersands when applied to types is: do they represent two different concepts depending on usage? Like I know that they indicate an rvalue reference, but they're also used for perfect forwarding. Those two concepts don't seem inherently connected to me, so the double ampersands appear to be overloaded here. Is that the case or is there some relation I'm missing?
I think of the rvalue reference as talking about the object itself rather than how you get to it (i.e. the actual bytes on a chip someplace as opposed to its address or a copy of an object). Hence moving, perfect forwarding, etc are roughly-synonymous ways to think of managing that object, maybe just looking at the same semantics from different angles.
This is just a conceptualisation, of course (rvalue definition is a little more subtle), but for me as a Lisp programmer it seems natural and intuitive. If you come from a different background or perspective I understand that it can seem weird — again, most languages are not as expressive.
The low level nature that is fundamental to C++, even when you are programming at a very high level, which you now conveniently can, is a consequence of it working hard to be a zero-cost-abstraction systems programming language, leads to issues like this one, or to some odd consequences of move constructors.
There are alternative points in the solution space. Compare it to go, which explicitly papers over some of these trade offs, making an opinionated choice instead, and being willing to embrace reduced expressive power (so there are some things you just can’t write in it). I don’t like go myself, but that doesn’t mean people who choose it are fools.
Yes. It normally indicates an rvalue reference, but in the context of deducing types (for example, in template instantiation or auto deduction), it can mean a forwarding reference and has a rather special meaning.[1][2]
For this particular "deducing this", it is actually removing unnecessary corner cases due to a wrong historical decision that made "this" parameter available only in an implicit way. Probably you may want to read the article before expanding your own narrative?
People complain about the breadth of C++, but the reality is that you don't need to adopt new versions, and regardless of what version you use when compiling code you should have an internal style guide dictating what features are permitted, which are not, and which features are only permitted in specific circumstances. For example, Google is switching from defaulting to -std=c++17 to -std=c++20 later this month, but most of the major C++20 features (e.g. ranges, coroutines) will be prohibited. And there are a lot of major C++ features that the Google internal style guide does allow but discourages people from using unless they have specific needs.
They do also deprecate and remove stuff too, and not all new versions are large (C++14 was very small). So, sure, they found a bunch of new stuff to add, but the frequency and size of updates does seem to depend on how much stuff is proposed that they deem useful enough to add.
Note that in the time C++ had 5 updates, Python has had 10 releases and Java has had 14!