back
84 comments
When we added the equivalent to Rust, it was a bit controversial. But the idea of "an optional type is a list with zero or one items in it" is much more normal in functional places, and so a lot of people like it quite a bit too.
The fun thing is that the original design (N3527 over a decade ago) for std::optional specifically says it's not the container with exactly zero or one items, whereas I believe Rust's Option never made this claim and so recognising this isn't a U-turn.
Scala has it like this since day one. Tried to convince D to adopt it, but was unsuccessful. Glad that C++ eventually got there…
In most respect it's also "day 1" in rust, since it was added in 0.7 (https://github.com/alexcrichton/rust/commit/4f2f545ac2ce1903...)
Not so fun for me. I wasted a considerable amount of time debugging an issue like this because i had returned option of vec.

Granted i have learnt better and should probably create an enum that explicitly describe what all the different states represent.

I really like how some good structures used by other languages, specially Rust and Zig, have been added to the newer C++ standard.

The Result, Optional, and Variant are really sweet for day-to-day use of the language, and those in-process standard libraries of SIMD operations, BLAS mathematical functions, and the the execution library looks really cool, specially as standard.

I would like C++ would be a little more "batteries included" in some ways, like having a basic standard for signals, networking (just handling sockets would be a huge thing), and some basic system calls.

> I really like how some good structures used by other languages, specially Rust and Zig, have been added to the newer C++ standard. The Result, Optional, and Variant are really sweet for day-to-day use of the language, and those in-process standard libraries of SIMD operations, BLAS mathematical functions, and the the execution library looks really cool, specially as standard.

for Optional and Variant they both were basically standardized versions of boost.optional & boost.variant, which exist since 2003 and 2002 respectively. Most of the time you can just change boost:: to std:: and it works exactly the same ; for many years software I develop could switch from one to another with a simple #ifdef due to platforms not supporting std::optional entirely (older macOS versions, pre 10.14 IIRC)

Often the std flavored implementation is inferior of the boost one. Support for optional references has only be added to the draft standard recently, while bossy has had it since forever.
Correct, they have been around for a lot longer than rust.
I knew some changes (like STL containers) came from Boost, but I didn't know those also came from there, and specially since such a long time!

That means I need to look more Boost documentation :)

"I would like C++ would be a little more "batteries included" in some ways, like having a basic standard for signals, networking (just handling sockets would be a huge thing), and some basic system calls."

Besides basic handling of TCP sockets and the Unix-style "Ctrl-c" keyboard interrupt, none of the stuff you're asking for is portable across different platforms. I'm not saying it's a bad idea, just that there is no one single universal standard for what an OS should do and what knobs and levers it should expose, or at least one that everybody follows.

Linux has non-trivial deviations from the POSIX spec, and even FreeBSD and OpenBSD have deviations. POSIX has its own compliance test suite that it runs to award certification of compliance, but it's not open source and it you need to pay a fee for it.

All of that however, is a drop in the bucket compared to making an API that exposes all the knobs and levers you want in a way that behaves exactly the same on Windows which barely has any architectural resemblance to UNIX. For exmaple, NTFS is case-insensitive by default and has nothing resembling the UNIX style of file permissions. Or more importantly, signals do not exist on Windows; something resembling signals for keyboard interrupts exists, but stuff like SIGHUP and SIGBUS does not. I'm talking the kind of known caveats that come with using a POSIX-compatibility layer on Windows, e.g. Cygwin.

I think if I get much deeper than that I'm just being pedantic, but even Python code behaves differently on Windows than it does on all the POSIX-like OSes out there.

Asilo Is a portable and efficient network abstraction. It was being tweaked for standardization for the last 15 years, before being suddenly voted out.
There's no universally adopted OS standard for a lot of the stuff in the stdlib but C++ sans std::string, a large portion of things under std::ios_base, most all of concurrency (e.g. std::thread), std::filesystem, and so on would be relatively shit in comparison.

As much as possible in the stdlib should behave the same across as many targets as possible. That's about where the relevance ends in my mind.

I knew about the difference they have between UNIX-like OSs in the usage of different signals (and the System V vs BSD battles, between others), but I didn't know Windows didn't have a similar system (I haven't done too much low-level in Windows).

Thanks for the long comment!

Some of them date back from way before Rust and Zig. I am thinking about Qt and Boost.

Boost in particular is like a testing ground for future C++ standards, with many of the "batteries" you want included. And it is already C++.

Of course, Rust is a huge influence nowadays, and it sparks a lot of debates on the direction C++ should take. I think less so with Zig, which is more C than C++ in spirit, but every good idea is good to take.

> I would like C++ would be a little more "batteries included" in some ways, like having a basic standard for signals, networking (just handling sockets would be a huge thing), and some basic system calls.

Isn't Boost library basically that? C++ has been slowly adopting freatures from it to its standard library.

C++ has been adopting features from a lot of different libraries into the stdlib. These libraries don't always do it "The Boost Way™", even when Boost has an equivalent library. Boost has a lot of good stuff though, it's just a little farther from being "std++, why care about std?" than commonly advertised.
I wish C++'s optional was less of a compromise. It would be great if it had specialization for sentinel values, like Rust. As-is it can be pretty wasteful in data structures.
std::variant is an abomination that should never be used by anyone ever. Everything about it is sooooo bad.
That's a bit hyperbolic. Sure, it's not exactly ergonomic, but that doesn't mean I can't use it. One thing that bugs me is that there is still no overload helper in the standard:

    // helper type for the visitor
    template<class... Ts>
    struct overloads : Ts... { using Ts::operator()...; };
Of course, having true pattern matching would be much nicer. At least there's a proposal: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p26...
Kill me, but I never saw the point over abstract base classes and polymorphic behavior defined in the subclasses.

Now purists will scream “compile time optimization!”, but in reality std::variant is inplemeted very múch líne a vtable. There was a good talk at Cppcon a few years ago on this issue. In particular, they found no difference in performance.

out of curiosity, can you elaborate on that a bit? I've shipped std::variant in a few designs and it's been OK, but maybe I didn't use it deeply enough to see issues.
Herb Sutter calls this kind of thing "spelling things generically" meaning that the same generic code will compile against different types. In this case the same for loop code will compile against a type that may hold zero or one items and a type that may contain 0->n items. Maybe this pattern could be extended to shared_ptr for example.
It's sort of like a cleaner approach to a C# SelectMany where you return either an empty array or a single-item array?

C++ keeps getting more stuff but it's clear a lot of thought goes into these additions. I think a "living" language has proven better over time (C# evolved heavily and borrowed from other languages, C++ does as well, maybe lately concepts from functional and Rust?) Go might be the big exception that evolves super-slowly.

The for syntax over optional seems unnatural. I get that optional is like a list of 0 or 1 owns. Is there a fault in my reading that makes the syntax clunky?
Putting it through a for loop demonstrates that it is iterable, imo.

  template <typename T>
  void foo(const T& container) {
    for (auto value : container) {
      // process value
    }
  }

  foo(std::vector<int>())
  foo(std::optional<int>())
What seems weird here? Iterating or mapping over Options is pretty normal in functional languages.
Well, you can map over options/maybes. For-loop style iteration over an option does seem a little strange IMO, first, because syntactically it looks like overkill, and second (and more importantly), because map evals to a value, while `for` does not.

But I suppose in C++, given the centrality of looping constructs, this is what you would do to accommodate option.

You're comparing apples to oranges. In pure functional languages, Option is a fundamental type. It's a concept introduced very early on when learning FP. Historically it had the same behavior and definition.

Trying to gaslight ppl to question their reasonable reaction of std::optional transitioning from its previous behavior to an Option like behavior when no other type in C++ behaves as such is disingenuous.

Of course it is weird in the C++ context, but is it a step in the right direction? absolutely!

With syntax questions, it can help to get very concrete. There's a few different bits of syntax in this post, what is it you find clunky?
Not the person you were replying to, but

  for (auto l : logger) {
      l.log(data);
  }
bent my brain for a moment. `logger` is a list of loggers to send data do? Oh, no, it's either 0 or 1 loggers.

Rust's `if let` syntax maps much more closely to how I think about it. I guess if this becomes idiomatic C++ then it'd start looking perfectly normal to everyone, but it still seems odd. For instance, I don't think I could ever bring myself to write Python like this, even if it worked:

  def do_something(data, logger=None):
      for l in logger:
          l.log(data)
Yeah, outside of templates it probably makes sense just to stick to if-style syntax. It's not any more verbose than the for-loop syntax.

    if (x.has_value()) {
      doSomething(*x);
    }
Seems like a good thing broadly. I haven't personally found a use for ranges, ever, but this seems consistent.
I've been using ranges fairly often but it's only for stuff like this:

    auto it = std::ranges::find(ctx.users, req_user_id, &User::user_id);
If ctx.users is a vector<Users>, it will return an iterator pointing to the first matching User, by matching a field in User to a variable.

It's just syntax sugar for std::find and a simple lambda but it's been really nice syntax sugar.

The example program seems confusing and pointless. Who uses the bell for debug logging? I found it distracted from the central point of the article as I stared at this bizarre program.
I don't know which I want to see happen during my lifetime more: Aliens making public contact or C++ being laid to rest.
Great to see they learned from Java, which initially made the mistake of not supporting the streams interface for their Optional type at first [1]. It was infuriating.

[1] https://stackoverflow.com/questions/22725537/using-java-8s-o...

Maybe somewhere in C++60 we will finally have proper monads
I do enjoy seeing C++ pick up these features from other languages.

... it's unfortunate that the feature then ends up in C++ syntax, which is increasingly divorced from the way it's used today, the kind of thing that nobody would have written from scratch if they were starting day-1 with these ideas in play. I look forward to having the features, but not to having to read things like `const auto flt = [&](int i) -> std::optional<int>` or `for (auto i : std::views::iota(1, 10) | std::views::transform(flt))`.

you can pipe from a for statement now?!?
The pipe is entirely inside the for loop in the example.
> the people writing the standard are not exactly known for adding features “just because”

Ah yes, C++, the discerning language.

Iterating over optional does seem syntactically convenient. My main question would be if it guarantees no overhead. For example, is there an additional conditional branch due to the iterator hiding the statically know fact that there's at most one iteration? I don't use C++ for its beauty, I use it for speed.

No, the generated code seems to be mostly the same as the manual version: https://gcc.godbolt.org/z/aK8orbKE8

The main difference there seems to be that GCC treats the if() as unlikely to be taken while the for() as likely.

> Ah yes, C++, the discerning language.

C++, the language that refused to add `contains()` to maps until, you know, C++20!

They didn't stop there, C++23 brought contains() to strings too! You can do some crazy stuff nowadays like say if(username.contains("hello"))

Absolutely incredible, definitely worth the wait

This is absolutely magnificent. Game-changing improvement, really shows how amazing the C++ committees are working.
It's hard to tell if you are being serious or joking. And I say that as a C++ aficionado :)
I'm sensing a pinch of sarcasm