Hacker News new | past | comments | ask | show | jobs | submit login
Learn Modern C++ (learnmoderncpp.com)
204 points by nalgeon on Dec 26, 2023 | hide | past | favorite | 202 comments



The tutorial starts with

    cout << "Hello, World!" << '\n';
But the most "modern" way to write to the console is:

    std::println("Hello World!");


I immediately thought your critique would be to use std::endl instead of '\n'

I was reading a lot of c++ core guidelines and modern c++ blogs and books while writing a little game a couple of years ago and std::println was definitely not the common suggestion for modern c++. I see it's probably because it wasn't even available at the time. Now I feel like I'd have to review everything that I thought was modern two years ago to confirm that it is still the modern way.


> I was reading a lot of c++ core guidelines and modern c++ blogs and books while writing a little game a couple of years ago and std::println was definitely not the common suggestion for modern c++.

Probably because it has only been available since C++23: https://en.cppreference.com/w/cpp/io/println.


std::endl is actually not advised most of the time. Using \n is the better alternative unless you need a buffer flush.


If stdout is going to a terminal (i.e., it hasn't been redirected to a file), then the standard library will by default flush on every '\n' anyway. Similarly for C with fputs, etc.

In C you control this with setvbuf, and of course, in C++ with iostreams it's a huge mess of rdbuf spaghetti and probably involving std::ios_base::sync_with_stdio as well.


This is wrong, stdout is not flushed by default on every newline.

You can observe this by printing out tens of thousands of lines with vs without explicit flushing, there will be a big performance difference.


Edit: My C preference and iostream hatred is showing again, because I answered a question that was mostly about C++ iostreams with results based around C stdio (I think iostream is possibly the worst standard IO API in existence for a popular language, so when using C++, I recommend using something custom calling out to OS syscalls or C stdio). I could easily be wrong about C++ iostreams, so the most relevant half of my original post is highly suspect. I'll test it out later, but my C stdio answer is as-is below.

I've tested that I'm correct on glibc, where the behavior is documented here: https://www.gnu.org/software/libc/manual/html_node/Buffering...

I can't remember using an alternative platform that behaves differently.

A better test than the one you ran is to look at the resulting syscalls from a loop of `fputs("a line of output.\n", stdout);`, vs. `fputs("a bit of output. ", stdout);`. Buffering accumulates strings in memory before an eventual write syscall, so looking at syscalls is an easy way to see the difference: a write per-'\n' means line buffering.

Compare the syscalls of both using strace. I did so, and when stdout is to the terminal, I see lots of calls to `write(1, "a line of output\n", 17)` compared to `write(1, "a bit of output. a bit of output"..., 1024)`. (To confirm, manually insert an `fflush(stdout);` in the "a bit of output. " version, and you'll see lots of `write(1, "a bit of output. ", 17)`)

Then compare both when redirecting stdout to a file, and you'll see both cases make large write-s of 4KB. Only adding explicit fflush-es will make either version go back to small write-s of 17B.

Compiled the trivial test programs with `cc -O2 ./main.c -o ./main`


Tested g++/libstdc++ and clang++/libc++ and both operate identically to the C test above, testing `std::cout << "a line of output\n";` vs. `std::cout << "a bit of output. ";` (and with explicit flushes using `std::cout << "a line of output" << std::endl;` and std::cout << "a bit of output. " << std::flush;).

So I'm correct on those targets as well.


This is wrong.

Looking for write syscalls is not a valid way to figure out where buffering is happening.

Write by default is famously buffered in Linux, and stdio/iostresm functions map closely to it. So, if you call these C functions N times, you'll see write being called N times.

Flushing calls fsync or some ioctl depending on the file descriptor is, iirc.


fflush and std::endl/std::flush are not synonymous with the fsync syscall. None of the standard libraries I tested above call fsync at all when using those functions. They flush the accumulated memory "to the file" in the sense of making a write syscall (or equivalent, e.g., writev), and they do not attempt to enforce a flush to disk, that's just left to the OS. The "line buffering" in question is the C/C++ library accumulated memory, and the relevant kind of "unbuffering/flushing" is just write-like syscalls.

I meant to reply much sooner, with more info from empirical tests. But now I'm happy to leave it at just the write stuff and the note about the meaning of stdio/iostream "flushing".

I think you have an incorrect or esoteric understanding of what the "buffering" in question is, but it doesn't matter to me. My argument is about what syscalls happen, and I don't care if you disagree with the (I think, standard) descriptor/terminology I'm using.


\n is a newline, endl is \n plus flushing stdout.


Maybe one day someone will rate all "Modern C++" tutorials according to their level of modernity :)


It's like modern art at this point, anything from the 1800s qualifies.


We are already on post-neo-modernism.


Maybe 'contemporary' would be better for this context.


What is the rationale behind changing how print statements work in C++? Is there something broken about using cout or is this just another case of C++ adding extra ways to do the same thing for the sake of adding extra ways to do the same thing?

The only significant difference I see is that std::println automatically includes the newline which may or may not be desired behavior.


What's fun is, because everything is decided in papers, we can find out why! https://github.com/cplusplus/papers/issues/884

Accepted paper here: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p20...

> The proposed std::print function improves usability, avoids allocating a temporary std::string object and calling operator<< which performs formatted I/O on text that is already formatted. The number of function calls is reduced to one which, together with std::vformat-like type erasure, results in much smaller binary code (see § 13 Binary code).

Additionally,

> Another problem is formatting of Unicode text:

> std::cout << "Привет, κόσμος!";

> If the source and execution encoding is UTF-8 this will produce the expected output on most GNU/Linux and macOS systems. Unfortunately on Windows it is almost guaranteed to produce mojibake despite the fact that the system is fully capable of printing Unicode


Whoever thought that the idiomatic approach to "Hello, World!" in their language ought to involve using special overloaded operators as symbols to act on pipes must've been on something very strange.


I don't disagree that iostreams feels like a mistake in retrospect, though you have to put yourself in the time period to understand why people make decisions the way that they do. Bjarne wrote the first version of that library in 1984! And it was moved into the standard in '98.

https://stackoverflow.com/a/4854358/24817 is the canonical explanation for why >> and <<, apparently. Nearby, this is also mentioned:

> C’s printf family of functions is an effective and often convenient I/O mechanism. It is not, however, type safe or extensible to user− defined types (classes). Consequently, I started looking for a type safe, terse, extensible, and efficient alternative to the printf family. Part of the inspiration came from the last page and a half of the Ada Rationale [Ichbiah,1979], which is an argument that you cannot have a terse and type− safe I/O library without special language features to support it. I took that as a challenge. The result was the stream I/O library that was first implemented in 1984 and presented in [Stroustrup,1985].

and

> The stream I/O facility is implemented exclusively using language features available to every C++ programmer. Like C, C++ does not have any I/O facilities built into the language.

The language simply didn't have the advanced tools that ended up being used in more modern solutions yet. It was too early.


It is weird but not unique to C++. For example, python used the % operator for string formatting. It's a simple but hacky way to feed arbitrary number of arguments in a type-safe way if you don't have variadics.


I know you think this sounds like a good reason, but all I can think is that C++'s continues its mutation into a new language and adding to the developer's cognitive load, all for the purpose of a minor performance optimization. In what is already a very performant language.

As usual, C++'s priorities are wrong for anyone who's not writing an OS, web browser, game engine, or otherwise rare type of project.


I agree with you that evolving languages that have been around for a long time is very difficult, with hard tradeoffs and unclear answers.

I think calling formatted printing “cognitive load” is a bit much though. If anything this is easier to understand and use than stdio and iostreams. It’s closer to other languages in this area. I probably would have voted yes on this if I were on the relevant committee.


There are numerous shortcoming of the previous (iostreams) approach:

1. Incompatible with dynamically-generated format strings, in which the order of arguments is different. Example:

   std::cout << month << '/' << day << '/' << year
... but you now want to adapt that for a non-US locale, e.g. a European one where it's

   std::cout << day << '/' << month << '/' << year
with iostreams, you have to change the code. With C-style printf, you don't (but it's not typeafe and not flexible. But with std::print it could be:

   std::printf(my_format_str, day, month, year);
and `my_format_str` could be either "{0}/{1}/{2}" or "{1}/{0}/{2}".

2. The string allocations which other have mentioned.

3. A lot more typing that is easy to confuse: " << ".

4. The iostreams implementation is super-baroque, with buffer classes nobody uses.

and maybe I'm forgetting things.


Std:so std:you std:mix std:iosteam std:and std:printf?

Std:seems std:confusing


20 years ago, I worked on a fairly large C++ code base. Nobody used iostreams even then. We used C-style stdio everywhere. You can still do that.


I have been using iostreams since 1993, they were never the cause for performance problems we actually had to solve.


You've never had to deal with binary size concerns I suppose.


You never coded for MS-DOS and Amiga, I suppose.


Glad you asked. I recently wrote a post about it: https://vitaut.net/posts/2023/print-in-cpp23/.


iostreams were a mistake, is the shortest way to sum it up.


Not for everyone, iostreams are great.


Absolutely! Glad to see it ending


Did you mean endling?


No, he meant backslashend.


As you may imagine, the rationale behind offering a new way to do formatted output is not evidenced in a hello world statement.......

You can answer your own question if you google "println cppreference".


There's std::print


That doesnt work on a majority of systems yet. Heck, gcc 14 gets it in the 14.1 release next year.


True. This is not a modern C++. It is a futuristic C++. Use {fmt} library in the meantime.


I feel modern c++ is not so much mew features, save a few like move, but embracing guarantees with things like raii types.


This comment right here is modern C++ in a nutshell. The most "modern" way will likely be different next year.


But either way is fine.


That's the modern-modern C++ :-)

More seriously, though - the language is changing gradually: Features are introduced (and rarely, deprecated); and the missing bits of the standard library are added. C++11 was a _very_ significant change - but not to how you print output. C++20 and C++23 introduced `std::format` and `std::print`.


C++20 is as big a change as C++11. It is almost as much more pleasant upgrading to C++20 as was upgrading to C++11. As more of the C++20 features get implemented in the compiler you use, the experience will continue to improve.

Many people can already write "import <std>;" instead of many lines of "#include <...>".


its `import std;`, without the angled brackets. Just make sure you are using the namespaced fixed integer types if you do :) else you still need `import std.compat` which is basically the c-headers


This is from C++23, right? I wonder how available this is within compilers and how many shops plan on moving to this version of the standard and how long that will take.

I imagine targeting C++17 or 20 for 'modern' would be practical enough considering thats what you are most likely to run into in a professional setting.


> This is from C++23, right?

std::println is, yes.

> I wonder how available this is within compilers

https://en.cppreference.com/w/cpp/compiler_support says clang, gcc, and msvc all support it, though I don't know how recent those versions are off the top of my head.

In my understanding, with this specific feature, if you want a polyfill for older compilers, or to use some more cutting-edge features that haven't been standardized yet, https://github.com/fmtlib/fmt is available to you.


> I don't know how recent those versions are

gcc 14 has not yet been released; going by past years, 14.1 should come out around the start of May (2024). clang 17 was released September (2023); note that you need to use libc++ (stlib=libc++), not libstdc++. VS 2022 17.7 has been out since August.


Thank you!


> says clang, gcc, and msvc all support it, though I don't know how recent those versions are off the top of my head

Where did you find it? Search by println yields no results.


  Formatted output library <print>


Thanks!


Honestly C++ is in a real pickle.

So iostreams were a mistake, they shouldn’t have happened in the first place. And given that they happened, std::format should have been added 20 years ago, or at least in C++11.

The problem is that many professional shops never used iostreams, and instead hand rolled their own format library. Now, they don’t really care about std::format, and probably will never use it, because it would involve migrating off of their hand rolled solution which is working great.

On the other hand, for small shops and hobbyists that don’t have the resources to reimplement std, this is a huge quality of life improvement. Meanwhile, more modern languages like Rust are encroaching.

And this is all just about how to print “Hello World!”. This situation is really emblematic of the challenges faced by the C++ committee. It is really impossible to please everyone, or even a majority, since the community is so fractured.


This is not about printing hello world but about all formatted I/O which is a significant chunk of the library. To be fair formatted I/O is pretty broken in C as well but for completely different reasons.


God forbid it actually figures out what the type is and prints it out properly like every other language in existence? If so I'm sold.


Yes. The main feature this is built upon is C++20's new string formatting features (very heavily inspired by the third party libfmt). C++23 just adds convinience functions to format and print at the same time.

Type detection, formatting options, positional-arguments, custom formatters for custom types and probably more are all supported.


Sure, but that still doen't have compiler support in compilers people actually have


Not for portable code, C++23 is still quite far from being widely available.


You don’t need to keep up with the joneses to write C++. The whole point with the language is that it’s for millions of lines of code that exist for decades and you don’t update every line every time the standard is updated.

Most existing code probably uses output streams with << operators, it’s good to know what that does.


> But the most "modern" way to write to the console is:

Indeed, as mentioned [0] at cppcon 2022 :)

[0]: https://youtu.be/eD-ceG-oByA?list=PLHTh1InhhwT6c2JNtUiJkaH8Y...


I‘ve hated cout from day 0. happy to learn this. Thanks!


When did that happen? I used to write a lot of C++, but it's been years since I've had to use it everyday.


This year (C++23)


It has not happened yet...


Very Pascal-esque


See also "Modern C++ Programming Course" https://github.com/federico-busato/Modern-CPP-Programming


I feel that modern c++ is a game of how many times you can fit `const` in a single declaration. I usually have at least three for the simplest of functions.


Const in C++ is probably one of the biggest frustrations I have with the language. It's an API promise, but suffers from all the downfalls of "what colour are your functions". Combine that with aliasing, const_cast, mutable, you're left with a pinky promise, with code duplication that I honestly wonder sometimes if we're better off without


Then, in typical C++ form, I'll tell you that you are probably "doing it wrong". Just avoid const_cast and mutable in your application code altogether. Keep the const safety and the const will keep you safe :-) . Well, somewhat, granted. There is aliasing; and the fact that `restrict` is not formally part of the C++ is a pain point for sure.

As for the code duplication, that's partially alleviated by a language feature introduced in C++23:

deducing `this` https://youtu.be/eD-ceG-oByA?si=L5XIOpsQLYVT-laP&t=1045

and another part of it is addressed by adhering to the "rule of zero":

https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines...


The problem is that if I write

    struct Foo
    {
        int Bar(const Baz& b1, const Baz& b2) const;
        // ...
    };
The compiler can't reasonably do anything helpful with it _because_ of the above rules. Once you peek behind that curtain, it kind of crumbles.

> As for the code duplication, that's partially alleviated by a language feature introduced in C++23:

Agreed about deducing this, it's a bit awkward but solves a real pain point.


> The compiler can't reasonably do anything helpful with it _because_ of the above rules.

Well, you can delegate work to a function with `__restrict` on its parameters, and then the compiler _can_ help you. Though you would probably need to check that the _restrict_ is valid. Still, classes should probably not do the performance heavy-lifting.

... but then again on the other hand, `__restrict` is a compiler intrinsic, not really part of C++. That's a gaping hole right there if you ask me.


Well, it's a compiler intrinsic to expose a part of C that's missing in C++. It's not like it's something GCC or clang came up with. It's like `_Bool` and `_Generic`


what do you want that function to do? and why does const not let you?


You want to avoid reloading data all the time, but you can't because of aliasing


that doesn't really answer my question.


It does - when I call

    Func( foo, foo);
I don't want foo to be loaded twice.

Theres also the `const` on the function itself. If I do:

    struct Foo {
        int GetVal() const {
            return Val;
        }

        int Val
    };

    void Bar(const Foo& f){
        int val1 = f.GetVal();
        int val2 = f.GetVal();
        assert(val1 == val2);
    }
This is not guaranteed for a variety of reasons. This means that there are many optimisations that are just unavailable because of const, and there are guarantees that on first glance you expect to be true, but arent.

I understand why, we don't need to go into it, but its a mess that const doesn't actually mean constant.

It just means you've opted into coloured functions [0]. It's not quite as painful as async functions in js, but all the same arguments apply.

[0] https://journal.stuffwithstuff.com/2015/02/01/what-color-is-... t


As someone who strongly regrets the fact that most programming languages arbitrarily restrict valid identifiers to Unicode "word characters" (ID_START and ID_CONTINUE), I was pleasantly surprised to discover the other day that, like Racket, Clojure, Julia and Swift, you can use emojis and pretty much any Unicode symbols in C++ identifiers!


We walked that back, sorry. http://eel.is/c++draft/diff.cpp20.lex#1

  Change: Previously valid identifiers containing characters not present in UAX #44 properties XID_Start or XID_Continue, or not in Normalization Form C, are now rejected.
  Rationale: Prevent confusing characters in identifiers. Requiring normalization of names ensures consistent linker behavior.
  Effect on original feature: Some identifiers are no longer well-formed.


Arrg!! You can't rely on anyone these days. Oh well, at least these symbol-like characters which I've collected for use in Javascript (which has almost the same rules) should still work!

  ⴵ ⵛ ꘜ ⵣ ꕤ ꖜ 
  ꘖ ꧮ ⴲ ꘖ Ⰴ Ⰺ


When you are finished there, you can continue learning Modern C++ here:

https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines


why would someone learn c++ in 2024, assuming one is not about to work on legacy projects? I'm about to start a quixotic new personal project and was considering using (read: learning) c++ for it, but it feels like rust is getting all the new attention. (and I'm already spoiled by haskell and racket.)


Because interesting stuff is written in C and C++, far more than any other language in existence right now.

Compilers, games, all of the backends of the current LLM revolution, control systems for interesting hardware.

You would sabotage yourself if you choose not to learn how to use a possibly suboptimal tool that is an industry standard.


There are big parts of the industry where Rust still doesn't have much penetration, even for new projects. Games is a big example. Highly regulated, safety-critical code is another. I think Rust will eventually get there, but it'll be a while before it's a "boring" choice for these things.


You learn a new language to write programs in that language. Few want to pay you to code in Haskell, Rust, or Racket. It will be that way for a long, long time. In 2030 your list will be different, but people will still pay to have C++ code written. Your personal project might then turn out to be in what is considered a legacy language, as Ruby is now. But C++ will continue modernizing, and getting more pleasant to code in.


The state of programming languages on GPUs.


I have not done a "desktop" program in 25+ years and never using C++ (or C), since then I'm mostly a web developer (PHP, Elixir, JS, Kotlin etc).

I'm currently doing a C++ audio plugin with the Juce framework.

This website has been a good resource, alongside https://www.learncpp.com

But I was actually close to give up before using those two things:

- https://github.com/nlohmann/json : my plugin use a json api backend and the Juce json implementation is atrocious (apparently because of being born with a previous C++ version), but this library is GREAT.

- ChatGPT 4. I'm not sure I would have "succeeded" without it, at least not in a reasonable time frame. ChatGPT 3.5 is slow and does not give good results for my use case but 4 is impressive. And I use in a very dumb way, just posing question in the web UI. I probably could have it directly in MSVC?

Also I must say, for all its flaws, I have a renewed appreciation for doing UI on the web ;)


I’ve been using C++ since the late 80’s and for the last 15+ years it’s been reluctant and only due to the projects I’ve worked on and the people I’ve worked with.

I’d love to be in a position to work on a new project using Rust or Haskell instead.


Looks clean! Just was wondering which framework/service do you use for hosting these md files? I am asking as I find myself motivated to host my own site like this consisting of mdfile.


Soooo.. front and center of c++ ...

Are string literals only 8 bit chars aka no real Unicode? The description danced around it so much it sounds like obfuscation


Both C [1] and C++ [2] have string literal syntax for UTF-8, 16, and 32 strings.

[1] https://en.cppreference.com/w/c/language/string_literal

[2] https://en.cppreference.com/w/cpp/language/string_literal


I guess, we already have *www.learncpp.com* which includes most of the topics mentioned in moderncpp.com


Looking for resources on C++ for MacOS (GUI). Anyone?



Good if you like coding bugs.


then any books/resources u can recommend?


To write C is to code bugs. So, pick a different language.


quants use mainly or only c++


Right. Use C++.


It's extremely hard to write good tutorials. This one is very clear, but seems to have no time budget or reader model. I appreciate the clarity and the extensiveness, but it mainly makes me wish for something different.

Today there are so many IDE's and online coding environments, godbolt, Swift playgrounds, golang interactive tutorials, etc. It would be lovely to see a C++ tutorial join that trend.

I find the tutorial unsettling from the first words: `original, self-contained`. My first step in writing a tutorial would be to link the current state of the art, and the last would be a section pointing users to further resources, if my goal were not to trap readers but to help them. (Originality is a tall claim as the internet spawns LLMs.)

Without any orientation to the landscape, the tutorial proceeds step-by-step with topologically-sorted vignettes that explain themselves verbosely.

hello-world is prefixed by a long explanation why people start with hello-world, and following by 16 paragraphs of (to me) excruciating hand-holding and suggested experiments, without re-quoting the code. It's like pair-programming with someone who tells you what to type.

The original K&R book was breathtakingly brief, mainly just showing you how to do things. Effective C++ neatly crystallized specific problems and solutions. Both benefited most by what they left out.

Outside of an interactive tutorial for newbie's, what's needed for "modern" C++ is an origin story for each feature: what motivated it, how backwards-compatibility shaped it, what design decisions were made, how well it has been implemented and used -- ideally with bonus comparisons how Rust and Swift and Go managed the same issues. I think that would help people remember the complex issues and the syntax, and how to use it.

To me most of the discussion from the C++ originators is more expository than explanatory: `for each opinion, explain in detail with cross-references to other opinions` - the political template. But readers only need to know the distinctions that make a difference in when and how they use a feature.

Actual users are busy and paid only to get things done, not sling words. Users who value their time will pay for a good resource.


Rust posts on HN generate drama because of Rust People bickering with Anti-Rust People.

C++ posts on HN generate drama because of C++ People bickering with C++ People.


C++:

Do I contradict myself?

Very well then I contradict myself,

(I am large, I contain multitudes.)


A foolish consistency is the hobgoblin of little minds, adored by little statesmen and philosophers and divines.


very true, which is one reason why I keep using c++


Honest question here. How hard and frustrating is it to keep up with all this junk?

As someone familiar with nearly a dozen scripting languages, I keep trying for something low level like C++, but it seems like these languages are impenetrable at times. It's hard enough to learn the machine stuff and manual memory management, but having the syntax change more often than a toddler's favorite color just makes it all seem impossible.


Tried C++ once and only once. After the compiler spit pages and pages of alien and incomprehensible errors I discovered that the language was not for me (I felt like I was battling against the language and the computer). Never looked back nor regretted about it. In my opinion is way way better to learn modern C (and good C practices) a zillion times.

I think that Ken Thompson nailed it on C++: https://en.wikiquote.org/wiki/Ken_Thompson#%22Coders_At_Work...

Also Linus Torvalds is a huge detractor of C++: http://harmful.cat-v.org/software/c++/linus

But in the end everyone should pick whatever programming language feel comfortable with. Sometimes if you're pursuing a career in certain directions (e.g: game development) you have literally no choice but to knee down and learn whatever the industry is pushing to use.


You just read the first error message, and find where it says "at line #N" to see where the problem is. You can ignore everything after, however many pages you get, which is usually just a list of the functions you might have been trying to call, and can be long if there were lots of possibilities.

Linux Torvalds has not looked at C++ since the '90s. C++ is a wholly different language now. The experience coding in it now much more fun, and it is easy now to write code that works the first time it is run (as is also seen with Rust).


I wish. I had an issue where I accidentally passed a double literal instead of a float literal and the error was longer than my terminal history (so more than 10k lines) and even when I actually got the full message by piping it into less it didn't point at the correct line. (Clang did though so clangd could spot it)


I'd say this is the typical case for most languages, especially if they try to compile the entire codebase and only report errors at the end.

Take TypeScript, for example: if you bugger up a type definition or function call in one file, you'll have to wade through the errors of everything depending on it. Oftentimes you can go from 30 errors to just 2 or 3 with one simple change.

The alternative is they fail at the first error but you don't get the root cause and end up fixing shit one by one if the problem isn't obvious.

Especially with a new project in an unfamiliar language - start small and compile small, incremental changes. You won't end up with something you can't fix, which can't be said for installing dozens of dependencies up front and hoping for the best.


When working with code with multiline macros, or complex templates, it is impossible to understand some errors. OP is gold right in that. C++ is so complicated, it is just impossible to make an efficient compiler that gives goos error messages. Also having to ignore the pages after one error, to me, is a shittines indicator.


Multiline macros and complex templates are rare in modern C++. What you describe is workarounds for features the language used to lack.


I describe working with real life code, where there are many old libs and code, often in plain C, with which you have to interface with.


The GNU g++ compiler generates notoriously bad error messages, although they have gotten a little better, and you do get used to them (a bit like reading the entrails of a sacrificial victim to divine the future).

The clang++ compiler, much more modern and built on the LLVM inftrastrucure, has much better error reporting. Occasionally, in desperation, I've compiled something with clang++ just to see the error message in cases where g++ was just too unhelpful.


I think in any case you need to learn C before C++. As a language C++ is a footgun. Unless you really want to learn it or need it for a job, it’s not something you pick up for fun. If you want to do computer graphics or computational geometry it’s best to learn the language - not for it’s own sake but because so much of the domain works in it.


No. Starting with C is the way to lock in bad habits and buggy coding. Writing fully modern C++ eliminates myriad sources of bugs and security holes.


And adds many other problems. No silver bullet.


New features pass a very strict gauntlet; they have to solve common, serious problems. Using the new features eliminates exactly those problems.


One can write very nice code in C and modern tools also eliminate a myriad sources of bugs. Modern C++ can be nice but adds a lot of complexity. I am much happier with C.


A real solution to those voluminous inscrutable compiler diagnostics is to paste it into ChatGPT; I've found it works absolute wonders, really distills the exact issue at hand.


I am not a C++ programmer, but I try and "keep up with all this junk" and also care a lot about how people learn programming.

Programmers like really neat logical explanations for everything, but the real world is much messier. People do not acquire human language skills by learning a formal grammar, learning all the edge cases, and then finally speaking: they attempt to communicate as best they understand, and then refine their knowledge over time.

The same works with computer languages. Not just C++. You don't generally learn every single detail before you do meaningful work. You just kinda try stuff and figure things out. Sure, you may practice specific skills, but that's just not the primary method of learning. And even in those cases, you don't necessarily learn everything before you return to practicing what you've learned: it's a process where the two activities feed into each other.

You mostly just go "damn I wish there was a better way to do X" and then find out there's a paper someone wrote, and then you pay attention to its progress. Or you just keep doing what you're doing, and at some point, a blog post comes out that's a summary of what's new, and you go "ooh that sounds interesting, I should look into that." But for every feature like that, there's also features which you go "ehh I don't need to care." And that's generally fine.

> having the syntax change more often than a toddler's favorite color

No real syntax is different here, this is a function call like any other. And C++ changes on a three year timescale. Many scripting languages release new versions far faster. It's just stuff that's less familiar to you, and so it seems harder, but it's learnable.

> It's hard enough to learn the machine stuff and manual memory management

This is the bigger struggle with learning, moreso than "syntax changes." If you haven't done lower level stuff before, you also have to recon with that while learning. That does make things harder the first time you learn a new paradigm, just like if you'd never used a functional language before, you'd be learning about those concepts as well as the syntax and specific language semantics.


You shouldnt need to ”keep up with a language”. A professional computer language should always have a ”LTS” mode where you can compile a decade old code without modifications. Otherwise the language is a toy.


> A professional computer language should always have a ”LTS” mode where you can compile a decade old code without modifications

Isn’t this the epitome of C++, though? It literally is the LTS language


You can write better code with the current language than you could with previous versions. So, keep up if you want to write better code. Stick with the old stuff if you don't. To me it is an easy choice.

Every new Standard has some features few people will use. Those are easy to identify and ignore if they don't solve a problem you have.


That is true, though in the real world (existing codebases) you encounter old, new, modern and novel styles of C++. So in order to understand the used forms of something that should be simple, like printing, you will need to know at least 4 different ways of coding it. That is somehow the frustrating part of the language, there is no 'right' C++. Kudos for those who are willing to dig deep enough to fully understand const correctness, smart pointers, dynamic casts, double referencing, every type of formatting and template programming. Though sometimes I think there is some masochism involved...


C++ is a relentlessly practical language, so your go-to for understanding what you find is practicality.


I'm pretty sure C++ is the most opposite you can get from being a toy language.


> You shouldnt need to ”keep up with a language”.

I never said you had to. The question that was asked is "how hard is it to" not "should I."

> A professional computer language should always have a ”LTS” mode where you can compile a decade old code without modifications. Otherwise the language is a toy.

I don't personally feel that this is a useful distinction because it means the vast majority of language implementations are a "toy." Heck, C++ might be one of the few non-dead languages where this is true! You can add new things while staying backwards compatible. "I want to keep up with the language" and "I want ten year old code to work" isn't inherently at odds.


Varies, but it is a layer of stress. I have around 10y in a field where getting rid of C++ is close to impossible and I'm more excited about maybe changing areas than spending ten more working with C++ codebases. The things that affect me the most are:

- No standard / user friendly tooling: there is no package manager, getting dependencies "as easy as copying a header from this github repo" is messy, is a liability, generates toil, etc.

- The language is very complex, i.e. there are 7 different ways just to declare a variable (I'm not even discussing templates, optionals and what not) and more often than not C++ projects spans years or decades, there is a natural mix of styles from people across different generations on what is "modern" and it's a sore to my eyes. At every release rarely things are removed from the language, but there is always something added.

At every company, the subset of what features are reasonable to use, understood and well maintained can change drastically. That might be a company problem, not a language one, but it's something sistematic enough that I would say it's a language thing.

- Every language has its gotchas, but with beginners interested in using fancy features of the language, the surface area is just too big.

I've read once on the blog of a former member of the C++ technical steering committee, that at his best, when actively working with the committee and focused on this, he maybe knew 50% of the language spec. What are the odds of the common folk knowing the pitfalls and good practices of the language and that staying relevant in 5-10 years?

Even though C++ is the language programmed the most in my career and I can appreciate how nice it is at the right conditions, I've come to accept that the tenets of the language, the way the TSC sees it (stability, backward compatibility, performance) no longer resonate with me and I come to appreciate the consistency, simplicity and ease of use of other projects, there is a bigger world out there and the pond of C++ issues is a place I would rather leave behind.


"I've read once on the blog of a former member of the C++ technical steering committee, that at his best, when actively working with the committee and focused on this, he maybe knew 50% of the language spec."

This isn't relevant. The standard is for compiler writers and library writers who use advanced meta-programming.

"What are the odds of the common folk knowing the pitfalls and good practices of the language and that staying relevant in 5-10 years?"

That depends. Some pitfalls just disappear like std::auto_ptr but I doubt anyone complains. Big changes came with c++11 and c++20, that is once in 10 years. While c++11 added move semantics and new initialization syntax that are somewhat complicated and hard to ignore, c++20's additions only make you glad that your life has become easier.


"This isn't relevant. The standard is for compiler writers and library writers who use advanced meta-programming."

It is. It is a metric of how ungodly complex the language is. You can infer by this that having a comprehensive mental model of the language is difficult, do's and don'ts are all over the place and even a thing "simple" as a cast can lead to UB, but this flies under the radar of a lot of developers. You don't need to be a compiler engineer to make good use of such information.

"That depends. Some pitfalls just disappear like std::auto_ptr but I doubt anyone complains. Big changes came with c++11 and c++20, that is once in 10 years. While c++11 added move semantics and new initialization syntax that are somewhat complicated and hard to ignore, c++20's additions only make you glad that your life has become easier."

True, but it takes time to be proeficient with the changesets, for the projects to be transitioned to newer versions, if they are at all, and then to map what is valid in which version if there are reworks everywhere (e.g. I'm involved with some that are still c++14, "Do we have structured bindings available here? Oh, no, too bad.").

I appreciate thing gettings better/easier, but c++ seems to be all over the place (just checked the compiler support for c++20 and there is still partial support for some features in major compilers, even though work on c++23 has already started).


It is ungodly complex, but no one needs to understand all this complexity just to use the language to write application code.

"do's and don'ts are all over the place and even a thing "simple" as a cast can lead to UB"

And most of these things were present in the first revision of the C++ standard or even in C.

"You don't need to be a compiler engineer to make good use of such information."

Why do you think that a programmer has to learn C++ by reading the standard? One does need to have a good mental model of what's happening but it's enough to have a much much simpler mental model than the standard.

"and then to map what is valid in which version"

That's where skills acquired 5-10 years ago are most relevant)


Application devs don't need to know the spec, but someone in the committee having a hard time of knowing the language in full is an interesting fact of how complex the language itself is.

"Why do you think that a programmer has to learn C++ by reading the standard?"

If your application seg faults due to an ill type deduction caused by `auto`, why did it fail? What kind of type deductions auto covers? Experience by trial and error will hardly help you here.

End user developers will acquire that knowledge probably by reading digested material from experts either focused on language tidbits or working closely to the standard (Effective C++, Effective Modern C++, blogs, etc.), the root source is still the spec, the more complex the spec is, by transitivity, the more complex is anything that comes out of it.

I guess the bottomline is that we agree the language is complex and I'll leave it at that.


"the more complex the spec is, by transitivity, the more complex is anything that comes out of it"

Yeah, that's why so many languages don't have spec at all.


Systems programming languages contain features most users don’t need but some users definitely require. Often those special features are needed to implement things in libraries so that ordinary user code doesn’t have to deal with certain low level details. Likewise ordinary user code doesn’t need to know about these new features!

And C++ is explicitly a multi-paradigm language so there can be affordances for multiple approaches. You can even use it as an OOP language if you want!

As far as syntax changes go, mostly they are in the direction of simplification and/or unification intended to make programming easier in the long run. So, for example, you can now in many cases write template generics without the elaborate template syntax.

But the C++ is also pretty unwilling to break old code, so old syntax and obsolete semantics are still available should you so choose. This is confusing to people who come to the language later on.

(Note that above I wrote “intended to make programming easier”. The case I cited of generics does make some things easier for me, but of course de gustibus and all that.)


"And C++ is explicitly a multi-paradigm language so there can be affordances for multiple approaches." - In other words, a jack of all trades and master of none. For a systems programming language, I'd want to be able to easily reason about where allocations, memory barriers etc are happening and I'd want an emphasis on correctness/verification (rather than ease of use). C++'s many abstractions, intended for applications programming, actually get in the way of systems programming.


Then use such a paradigm; it is explictly made possible.


This is essentially the same argument as "one can write secure software in C++". If it's not enforced by the language and tooling, one can't safely assume anything.


Use mechanisms that are safe.

You can write an incorrect program in any language. Some kind of self-discipline is important and necessary.


We just spent half a century proving that discipline never completely solved the problem. We can’t afford platforms in which a tiny mistake anywhere causes unpredictable failures in unrelated and correct code, and we can certainly afford the lifetime and bounds checks that prevent this.


So, use modern features that don't encourage mistakes. Most C bugs come from mistakes with pointers; in good modern C++ you rarely see pointers, and can afford to focus all the attention needed where they do appear.


I've never seen a C++ codebase that rarely uses pointers. Any good example?


I just glanced at Lohmann-JSON which just uses them for the json elements themselves (there’s a comment that says “raw pointers to save space” and spdlog and fmtlib which appear to use them only to interface to C APIs and *this. That was just a quick glance at some libs we use.

I have seen them used in the PIMPL idiom, which is a rather controlled case.


Here is a program that solves the New York Times puzzle "Letter Boxed". Its only uses of pointers are in processing main()'s arguments, which are provided via pointer, and mapping an input file, which Posix provides via a pointer. It does use std::string_view, which has pointers in it; the point is that you don't operate directly on the pointers, so cannot have bugs caused by misusing the pointers.

Try: "g++ -std=c++20 lb.cc && ./a.out iodhuamplrnc words".

  #include <array>
  #include <bit>  // popcount
  #include <iostream>
  #include <utility>
  #include <string_view>
  #include <vector>
  #include <unistd.h>
  #include <sys/mman.h>
  #include <sys/types.h>
  #include <sys/stat.h>
  #include <fcntl.h>
  
  struct Word {
    std::string_view str;
    unsigned bits{};
  
    static unsigned index(unsigned c) { return c - 'a'; };  // 'a'..'z' -> 0..25; others -> >25
    Word(std::string_view s, unsigned accept, long long sides) : str(s) {
      long long prev_side{-1}, side;  //  Values are 0..3. First letter gets a free pass.
      for (char c: s) {
        const auto ix = index(c);
        if (ix < 26 && (accept & (1u << ix)) && (side = ((sides >> 2*ix) & 0x3)) != prev_side) {
          bits |= (1u << ix), prev_side = side;
        } else { str = {}; return; }}}
  };
  
  int main(int ac, char** av) {
    auto usage = [av](){ std::cerr << "usage: " << av[0] << " abcdefghijkl [<wordlist>]\n"; };
    if (!(ac == 2 || ac == 3)) { return usage(), 1; }
    const std::array prefixes = { "", "/usr/share/dict/", "/usr/share/dict/american-english-" };
    const auto name = (ac == 3) ? av[2] : prefixes.back() + std::string{"large"};
    std::string_view file;
    for (auto pfx = 0u; pfx != prefixes.size(); ++pfx) {
      int fd = ::open((prefixes[pfx] + name).c_str(), O_RDONLY);
      std::size_t file_size = ::lseek(fd, 0, SEEK_END);
      void const* addr = ::mmap(nullptr, file_size, PROT_READ, MAP_SHARED, fd, 0);
      if (addr != MAP_FAILED) { file = {static_cast<char const*>(addr), file_size}; break; }
    }
    if (file.empty()) { return std::cerr << av[0] << ": cannot read " << name  << '\n', 3; }
  
    auto target_sum = 0u; auto sides_sum = 0ll;
    for (unsigned i{}, ix{}; i < 12 && (ix = Word::index(av[1][i])) < 26; ++i)
      { target_sum |= (1u << ix), sides_sum |= (i/3ll << 2*ix); }
    if (std::popcount(target_sum) != 12 || av[1][12] != '\0') { return usage(), 3; }
    const auto target = target_sum; const auto sides = sides_sum;
  
    const auto candidates = [target,sides,file] { std::array<std::vector<Word>, 26> candidates;
      for (std::string_view in = file, next; !(next = in.substr(0, in.find('\n'))).empty();
          in.remove_prefix(next.size() + (next.size() < in.size()))) {  // Skip past '\n' if present
        if (const Word word{next, target, sides}; word.str.size() >= 3)
          { candidates.at(Word::index(word.str.front())).push_back(word); }
      }
      return candidates;
    }();
    std::array<std::vector<std::array<std::string_view, 2>>, 100> answers; // radix sort by length
    for (auto const& firsts: candidates) for (const auto first: firsts) {
      for (const auto second: candidates.at(Word::index(first.str.back()))) {
        if ((first.bits | second.bits) == target) {
          answers.at(first.str.size() + second.str.size()).push_back({first.str, second.str}); }}
    }
    for (auto const& of_len_N: answers) for (const auto answer: of_len_N)
      { std::cout << answer[0] << ' ' << answer[1] << '\n'; }
  }


I don’t have a modern implementation handy, but according to https://en.cppreference.com/w/cpp/string/basic_string_view/d... a string_view depends on its data() pointer, and the lifetime of that buffer doesn’t seem to have guarantees.


Thus, the word "view". It refers to storage being managed by the creator of the string_view object, in this case a file mapped using mmap and not unmapped until after program termination.


"so cannot have bugs caused by misusing the pointers"

std::string_view lets you have some of the bugs that are caused by misusing pointers.


I'll answer from the perspective of someone who uses C++ for work and for some personal projects.

The answer is: Effectively not hard, because:

1. When you write complicated software - especially with older-standard-version C++ - you often have the thought of "Oh, if I could only do X more easily" or "wouldn't it be good if we could skip the tedious coding to make the compiler do Y?" - and then you either grit your teeth and write something ugly which works. But then, a few years later, you hear it mentioned that X' or Y' have been standardized, and then you know "Oh, good, now I can do that more easily".

2. Stuff that you _don't_ know about, that gets introduced - you typically don't have to know about. Either it's not relevant to you, or it is, but you're just writing old-style code.

3. The "junk" gradually makes you be able to write the code you want more easily. So, unless you want to do deep guru things - you can gradually even _forget_ things you used to know how to do, since you no longer need to know about them.


I write C++ as my day job. The secret is you don’t need keep up. The standard is weird and getting weirder. And no, it’s not ”well thought out complex technical endeavour”. It’s a jury rigged pile of legacy code with tons of useless sugar, somewhat modern features, and weird omissions.

C++ is a weird combination of pragmatic features, close to the metal performance (which you can junk if you don’t actually understand what is happening on machine kevel), universally abysmal tooling (compared to other current languages), huge ecosystem of super-usefull super-nontrivial libraries… a combination of super good and super bad.

If you don’t need the super good stuff (which you don’t need unless you know you need it) all you are left with is the bad stuff.

The C++11 is okay. C++ committee is a weird combination of clown-car non-savant idiots and super talented pros.


C++20 is as much more pleasant than C++11 as C++11 is than C++98. Restricting yourself to C++11 or (as some have promoted) C++14 or C is to write bad code where you could be writing good code.


Why do you feel the need to use every single feature? Are you constantly joining new projects that use vastly different features? Most programmers find a handful of features that gets the job done, and keep using them. Your knowledge from 10 years ago (C++11) is still valid and useful.


No, but I need to know them to read other people's code. Every C++ programmer has a slightly different subset of C++ features that they consider "basic", "fundamental", or "essential".

After 35 years of C++ on and off, I have pretty much given up it. I don't have the mental energy to deal with its complexity anymore. I will deal with parts of C++11 or C++14, mostly so that I can read other people's Arduino code.

I think I would rather learn a new language (Rust?) than deal with the hairball that is called "modern C++".


This 100%.

The vast majority of programmers' lives is spent reading code. We need to be able to interpret, repair and extend the work of others.

I often hear this defense of C++, that you only need to know certain parts and the rest are for "library writers". This is an absurd argument, but not exactly surprising from the core C++ cult(and I say this with 26 years as a C/C++ dev). It's almost as if there are two separate camps, one writing and maintaining production software, and another group which only works on language internals and is becoming further divorced from reality.


Some new features are specialized, like standard library support for SIMD instructions, and you learn them where you want that.

But many offer better ways to do familiar things, that make the overall programming experience more pleasant. Use those as soon as the compiler you use gets support for them. Often they make bugs harder to write, and correct code easier to write. Now we can write

  for (auto const& [key, value] : map) { ... }
instead of

  for (Map::const_iterator_type it = map.cbegin; it != map.cend; ++it) {
    Map::key_type const& key = it->first;
    Map::mapped_type const& value = it->second;
    ...
  }
It is just better in every way.

Another example is extension of "constexpr" to more and more of the language; each eliminates the need for some range of template metaprogramming. Nowadays you can ignore almost everything written about template metaprogramming, because there are good, straightforward ways to achieve its aims with ordinary language features.


Absolutely this, 100 times. I will never understand “don’t use all features” argument. Open 5 C++ projects side by side and you’ll find 5 different languages and 5 different build systems that are nigh impossible to build if they deal with dependencies.


Sadly agree. I used to know something that was called C++ (or at least parts of it) and even used to get paid to program in it. But now that C++ has become this kind of constantly morphing target (see the discussion here about what the most modern way to display text to stdout is) it's just tough to get my arms around it anymore. It seems like there was a noble attempt to modernize C++ and certainly there are some welcome parts of that, but it's gotten so far out of hand now. At some point you just have to admit defeat and start over from scratch with something that has a more unified vision. I'm looking at Zig and Carbon for lowlevel stuff and Julia for mathy stuff now.


It is easy: Ignore the old parts, use the new parts.


C++ is one of the oldest languages there is, it’s impossible to ignore old parts.


Yet, anywhere I have a choice between old and new, I choose the new, and do ignore the old. Except println(), hard pass.


I saw `<< std::endl` when my brain was still developing, and now it's too late to stop using it.


It is as easy to drop as it is to pick up "auto".


Can you give a real-world example? Most code-bases have massive inertia that resists complete re-writes using new features.


In one of Bjarne Stroustrup's books he recommends focusing on software engineering rather than learning every language feature, or something to that effect. I think it's good advice. (And it's a good coping thought, but no less true.)

An actual engineer or carpenter doesn't master every conceivable tool before beginning their work, they can't, there's too many.

Say what you will about C++ programmers, but at least they are free from the burden of trying to use a perfect language and can focus on more important things.


> Say what you will about C++ programmers, but at least they are free from the burden of trying to use a perfect language and can focus on more important things.

I would say most programmers, in any language, are free from that burden: once they picked their language they can focus on those important things.


If you want what you describe you use Java.


You don’t choose C++ because you want to but because you need to due to constraints that Java likely does not fullfill.

As a C++ dev - If you don’t need C++ then choose whatever other modern language.

We keep up with C++ because of specific constraints other languages don’t fill.


C++ is overwhelmingly more pleasant to code in than Java.

People use Java because they work in a Java shop. Similarly, COBOL.


Generally true, but if you start from scratch and only learn "the good parts" you have to be careful that you don't accidentally wander into some syntax you didn't know had some other use.


And if you end up learning something new it would be an absolute disaster /s

I learn something new all the time when I explore new codebases or even less beaten corners of existing codebases. Only a tiny fraction of the time it is a new facet of the language.


That doesn't happen.


My problem is more about how to get started and use certain scientific libraries. Maybe I just need to dive in. I keep getting detered though when I find out that simple things like iterating through a text file seem to take up so much code. Then I need a dictionary and somehow have to figure out how to get that. Not trying to whine. It's going to be a lot of work though lol.


GPT-4 can be a good help here. It mostly gets simple things right.


There's an experimental cpp2 "syntax reset" that aims to provide C++ but with just the modern/better defaults, which reduces the "junk", or needing to know what coding conventions to apply to make actual C++ not be full of foot guns.

Herb Sutter has been working on it for awhile. My only exposure to it has been watching this recent talk:

https://youtu.be/8U3hl8XMm8c?si=phHkBNFGaroYbeb9


I would start with C, then take it from there. Makes you think about memory management and pointers, and the language doesn't change much. C++ and Rust is a lot easier to process now that I know C, though they don't get prettier. Use ChatGPT for learning - it's good at writing and explaining C.


Starting from C is the worst possible choice. C-like code is the source of errors and security holes, just like actual C.

Instead, start using the most modern language your compiler supports. Everything you write will come much easier, with much less opportunity for mistakes, and be much more fun. You will hardly ever find yourself using pointers, and never allocate or free memory.


Well that's where the parent to my comment already is and still struggling with low level languages.


His mistake is treating it as a low-level language. C++ can be as low-level as you need it to be, but most of the time, no matter what you are doing, you don't need it to be.


> I would start with C, then take it from there.

I would be very careful with that approach. C is broken beyond repair, and C++ didn't fix it. The only sane way forward is a clean break with a cleaner syntax (or at least one that is truly context free, dammit), and fewer undefined behaviours. Much fewer.


Instead of going straight to the fix, like GC or RC, try and deal with it manually first. Try working in a language without templates/generics, figure out what that solves. And how does pointers work? You could figure it out in C++ for sure, but try C without all the cruft. It's a simple, lovely language on the surface. Use it to learn and move on to something else if you need to.


Are you assuming I didn't already do that? For your information I've written an entire cryptographic library in C https://monocypher.org and routinely chose C over C++. My claim that C is broken beyond repair doesn't come from ignorance or hype, it comes from over 15 years of first hand experience.

And of course, GC and RC aren't fixes, they can't apply in the performance constrained settings C and C++ typically are used for (tiny embedded chips, video games, video encoding…).

Also there's no way I'll even look at a new language without some form of generics. They're just too damn useful. Sure we could try the Go approach and special case generics for a few core data structures, but I believe a general purpose language needs a way to add custom ones. Heck, even Go fixed its mistakes and added generics after all.


Excellent, now let 7thaccount discover that. I suggest he or she starts with C.


Ah, I see I missed the context of your initial reply. If one's goal is to learn low-level languages, starting from C isn't such a bad choice (though I would still suggest moving away when possible).

But I (mistakenly) thought we were talking about designing a language. And for this I have a point: coming up with something like C in 2023 would be unacceptable. We know better now.

Sorry about the non-sequitur.


From my own experience, it seems a lot and overwhelming at first, but it quickly becomes natural the more you use it, just like with any other language. Plus like others said, you don't need to use every single feature out there.


A new version of C++ comes out every 3 years, and every 3 years you will get loads of blog posts giving overviews of the changes as well as YouTube videos of conference talks on different things, which I watch in general as part of my on-going learning and development.

C++ completed a cycle of related versions with C++11/14/17 so a lot of firms have settled on C++17 for now. C++20 introduced a LOT of new language features, some of which are still being explored and implemented and is still considered bleeding edge. C++23 has been finalised but is still going through the final ratification as far as I know.

It is absolutely fine to be a few years behind the curve, as unlike other languages (typically controlled by one company) where a new version ships at the same time as compiler/library support, for C++ the spec is released first and then the many different vendors implement it (in some cases approved features are implemented ahead of release - typically Microsoft gets library features implemented quick while other compilers get language features quick. Modules are the notable exception to this rule).


And this is currently starting to be problematic, when combining as much backwards compatibility as possible, the care about ABI, and some stuff that actually doesn't work in practice when compilers start catching up to ISO.


C with classes is peak C for me. C++ is best when it is fixing C, not when it tries to be something else entirely.


C is peak C for me. I think you can better modularize in C than in C++ because with APIs build around incomplete struct types the implementation stays in the C file and does not leak into the header as with classes.


You can use incomplete struct types in C++ as easily as in C. If you are hiding implementation, the struct will likely have a single data member

  struct private_impl_type;
  std::unique_ptr<private_impl_type> impl;
If you choose this, you trade off optimization opportunities. Your choice.


The last I checked in C++, you need to use the PIMPL (private implementation) idiom for that behavior: https://en.cppreference.com/w/cpp/language/pimpl

(not my favorite)


The C header interface is simple:

  struct foo;
  struct foo *foo_alloc();
  void foo_do_something(struct foo *p, ...);
This gives a simple to understand API with clearly defined boundary, has a stable ABI, preserves fast compilation times, avoids instruction bloat, etc. One could do this in C++ too, but what would be the point of using C++ then...


For me the only truly compelling C++ features are RAII and generics (not classes or virtual functions), and those are compelling enough for me to not consider C when I can choose C++. From what I can tell, the Rust designers seem to have felt the same way.


"C with classes" is a recipe for bugs and unpleasant coding. Lean into modern C++, and your correct, performant code comes easily.


100% agree!


[flagged]


I agree that modern c++ is messy, but I like this. It helps library writers, and empowers them to write better, safer code. I don't expect I'll ever need to write that function, but if it helps the standard library, QT, boost, gsl to write safer, more expressive code, im all for it.


(Context: https://learnmoderncpp.com/2022/12/07/applications-of-c23s-d...)

While the article describes why you'd want this abomination, honestly, I'd rather opt for the copy-pasted code

    T& at(size_t x, size_t y) { return data.at(y * X + x); }
    const T& at(size_t x, size_t y) const { return data.at(y * X + x); }
as something that's actually human-legible while also providing const-correctness. If the implementation is actually complicated enough, then sure, use the const_cast variation, but hide it away in a .cc file. This feels like a contrived example to show off new language features.

That said, I'd also rather use std::mdspan [0] if I had C++23 available, and not implement my own Matrix struct by hand.

[0] https://en.cppreference.com/w/cpp/container/mdspan


It isn't contrived, just incomplete. You'd need to copy-paste it four times -- there is &, &const, && and && const overloads (the fourth isn't making much sense, but still).


Make unique was something that you could sort of do in c++11 (with tiny header) and officially added in c++14.

All the other stuff you mentioned as far as I can tell is C++11 too, so it looks like you haven't used c++ for a long time.


That's not the point. The point is you shouldn't need that crap in the first place.


The use of ``this Self&& self`` in the ``at()`` method can appear to be an overkill. But this approach actually covers many scenarios and make the code much smaller. For details, you can read:

https://devblogs.microsoft.com/cppblog/cpp23-deducing-this/


I think you missed the point. The point isn't that these features don't have a purpose. The point is rather, at every line of code you have many choices that you arguably shouldn't need. Should I use a raw pointer or a unique_ptr or a smart_ptr. Do I need to call make_shared here or can I do something else? Should I call std::move here or not? Should pass by value or pointer or const pointer, or reference or const reference? And on and on, every line is a foot gun.

I get why it's that way, backward compatibility. The problem is, the original way, the path of least resistance, is now effectively deprecated, but it's the official syntax.

    char* s = malloc(size);
is considered bad code. I get why. But, in a "good language" the default would do the right thing and I'd only escape into bad code by extra work so that all the easiest code to write did the right thing by default.

C++ is trying to fix all that old bad code by coding standards and linters but I don't want to have to type a bunch of boilerplate I need to memorize to do the right thing. I want the right thing to be the most obvious, no brain cells required path.


Because a lot of things are not solved at the language level as the language is bloated enough as it is. Instead they are pushed into the library.

Of course if the language was designed again from scratch it might settle on a different local optimum.

From your example above, the only bit I really dislike is the handling of universal references (forward and &&), but that's the best that programmers better than me were able to retrofit onto the language, so eh.


std::forward<Self>(self)

ok that's pretty goofy


The C++ people can and will do whatever they feel like, I've come to accept that. Deep language models will bring clean, readable, literate code back eventually, but for now there's clearly no hope, and no point in complaining.

What bugs me is that if I ever need to update my resume again, I'll need to take C++ off of it. This time for sure. It's been there since the 1990s, but it has become increasingly fraudulent for me to leave C++ on my list of qualifications since then.

It kind of sucks to lose a hard-won skill... not because I got old and forgetful (which will happen, but not just yet), or because I stopped using it daily (I never stopped), but because they changed what it meant to "know C++." The code you posted might as well be Brainfuck or Perl or line noise or something.

C++ is simply not a C-derived language anymore, and we need to stop pretending otherwise. It was never about the brackets and semicolons, it was about the fact that you could read and understand it as a newbie with no more than a couple weeks' exposure. If all that crap is really necessary, then something went very wrong somewhere at some point, and went unaddressed afterward, and now it's too late.


It'd be nice to have next/previous links somewhere on the page to navigate between the entries which are meant to be read in order.


The introductory tutorial comes with a GitHub repo. Chad move. Makes it easy to put on my e-reader.

https://github.com/cpp-tutor/learnmoderncpp-tutorial




Consider applying for YC's Spring batch! Applications are open till Feb 11.

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

Search: