Hacker News new | past | comments | ask | show | jobs | submit login

Came here to post the same thing. C++11 was a major and practical step up from previous versions. I haven't seen anything in future standards that looked like it a tool I'd use day-to-day building actual production software. Much of the subsequent versions added things probably interesting to compiler and language academics. "Default constructible and assignable stateless lambdas?" Really?





Off the top of my head, C++17 brought slicker notation for nested namespaces, digit separators for numeric literals (so you can more easily read 1'000'000'000), improvements in type deduction for pairs / tuples (so std::make_pair / make_tuple are basically unnecessary now), guarantees in the standard for copy elision / return value optimization in specific circumstances,. Oh, and structured bindings (so you can now write `for (const auto& [key, value] : map) { ... }`).

edit: I guess digit separators came in C++14, I'm always a little fuzzy there since at work, we jumped straight from 11 -> 17.

C++20 brought a feature that C had decades prior: designated initializers, except it's in a slightly crappier form. Also, spaceship operator (three-way comparison).

Looking at cppreference, it looks like C++17 also brought if constexpr, and standardized a bunch of nonstandard compiler extensions like [[fallthrough]]. C++20 continued standardizing more of those extensions, and also brought concepts / constraints, which are a lot easier to use than template metaprogramming.

You're at least somewhat right though -- none of these are paradigm shifts as C++11 was compared to C++03 (especially with the notion of ownership, especially in the context of std::unique_ptr and std::move).


17 is worth it for std::filesystem alone. It also has optional and variant.

Filesystem is great. It’s insane it took so long.

Optional is nice but slightly awkward in a non-garbage collected language.

IMO variant is one of those things that should not exist in standard.

It tries to implement discriminated union in C++ but that feature is lame without true pattern matching. And you can’t implement pattern matching without thorough syntax level support. So in my books it’s in this academic “let’s pretend a while we are using some other language…” category.

It’s _occassionally_ convenient for sure.


> It tries to implement discriminated union in C++ but that feature is lame without true pattern matching. And you can’t implement pattern matching without thorough syntax level support. So in my books it’s in this academic “let’s pretend a while we are using some other language…” category.

I agree, they should have made it a language/syntax feature. However: if you wanna do a sum type, it does do that. I'd rather have that than nothing.


Might still land on C++26, but most likely C++29, then you'll have pattern matching.

Fingers crossed

std::filesystem is useless because like so many platform abstractions it doesn't handle the last 1% correctly.

IMO the C++ standard should focus platform-agnostic functionality and leave the nitty gritty platform interaction to standalone libraries that can be patched and/or replaced as needed.


Why does that make it useless? paths can be converted to basic_strings for passing to platform-specific functions to handle that 1%.

Designated initializers are so nice. Even though they have C++-isms like required order, it adds safety and readability to code.

The safety and readability are nice; but WHY do they have to be in order? That is so typically clueless. Such an obvious feature, screwed up in a way that only a C++ committee member could.

Initializers for members are _always_ run in the order the member is declared - this applies even in constructor initializing lists, see https://en.cppreference.com/w/cpp/language/constructor#:~:te... - it doesn't matter what order you declare the initializers in, and clang will warn you if you write your initializers in an order other than what they will be executed in. Designated initializers are the same way, it's probably best that the behavior is consistent across methods of constructing.

Why is it this way? As best as I can find it's so that destructors always run in reverse order of construction, I guess there could be some edge cases there that matter. It's not the strongest argument, but it's not nothing.


> Why is it this way? As best as I can find it's so that destructors always run in reverse order of construction, I guess there could be some edge cases there that matter. It's not the strongest argument, but it's not nothing.

I have seen my share of teardown races that were fixed by reordering data members. There's nothing quite like having a mutex torn down before the object that it's guarding.

Running destructors in reverse order of construction is really the only thing that makes sense in an RAII world. It's the same argument as running the destructors in reverse order of construction when exiting a scope.

That's still not a great reason for designated initializers being restricted in the same way they were in C90, especially given the advantage of hindsight. It makes a ton of sense if you have explicit dependencies between data members created during construction, but I can't see a way that you can create those dependencies with designated initializers.


Why does construction and destruction order need to be deterministic?

Well, consider what would happen if you had members whose value depends on other members. For example, one member is a pointer to another. Or perhaps one member uses RAII to hold a lock and another controls a resource.

Deterministic construction and destruction order is a fundamental feature of C++. Calling it clueless is just an indication one does not know C++.


> Why does construction and destruction order need to be deterministic?

That question presupposes a particular compiler implementation of designated initializers. Indeed, C90 had the fixed-order requirement until C99 decided this was unnecessary and removed it.

> Well, consider what would happen if you had members whose value depends on other members.

Can you specify a designated initializer in that way, though? Either you specify a value, or you don't; I'm not aware of a way to introduce dependencies between members with a designated initializer. Yes, you can add a default initializer to a specific member, but that only kicks in if it's unspecified by the designated initializer.

With a constructor initializer list, sure, you can absolutely introduce dependencies on previously-constructed members. But that's not the case with a designated initializer.


To me moving from C++11 to 17 and then 20 was just a matter of convenience. When digging on how to do this and that I've found few things that just saved my time here and there. Also couple of valuable libs I wanted to use required newer C++ versions.

> "Default constructible and assignable stateless lambdas?

this comes up surprisingly often.


constexpr in 11 vs 14 was night and day difference.

constexpr "if" statements (C++20) are also a game-changer.

Those are c++17

Initially, C++20 and C++23 extended its use cases, and combined with concepts is a pretty sweet spot for compile time metaprogramming without SFINAE or tag dispatch tricks.

Much better than having yet another syntax for macros.




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: