Hacker News new | past | comments | ask | show | jobs | submit login
C++ Frequently Questioned Answers (2009) (yosefk.com)
54 points by thriftwy on Jan 23, 2022 | hide | past | favorite | 110 comments



Some of these aren't true anymore. I'd say some things improved from that document. But, I'd also say a lot of things got worse that aren't mentioned.

We could nitpick at specific sentences/questions, but at the end of the day the spirit/gist of the document remains true, sadly.

I used to quite like C++ in my younger days (highschool, uni), but once you start working with multiple people on a team, or a larger project, the illusion quickly unravels.

The problem in C++ comes when you always have to make a poor value trade.

If you wanted readability, or maintainability you have to sacrifice performance.

Of you want performance, you have to sacrifice everything else.

A great and easy example of this is that the most succinct/readable usage of std::variant has the most morbid performance, and the visitor API is just... Well, if people find that elegant, then beauty really is in the eye of the beholder.

I still write C++ at my job, but I've no illusions about its grandeur.

I have met a few profiles of people in my life/career who defended C++, and they seem to be present on HN too, so please don't take these gross over-generalisations personally; they're imperfect as all generalisations are, and they're just anecdotal anyway:

Some just like feeling like powerful mystic wizards who have mastered the arcane syntax of invoking eldritch features. An additional layer is people who enjoys feeling like heroes when stuff goes wrong in production and they fix it with gdb keyslaps to dump core and transmute memory.

Another profile, that seems way more common, are people stuck with C++. It's kind of a sunk cost fallacy, they already have a career in C++. And there really was nothing better for a long time (ok there was, but not on the job market, and C++ is heavily lobbied at unis). It's also hard to know anything other than C++, because of how taxing the language is; there's little energy left in you to learn something else.


I moved into Java/.NET back in 2006, but still keep C++ skills relatively up to date, because for better or worse that is the native language I can use when plugging native libraries into them. No way I am going to use bare bones C unless it is a project requirement from customers or higher level.

Still, the improvements on those eco-systems, increasingly make it so that I need turn to it less and less.

C++ is one of my favourite languages, it was thanks to it I some major milestones in my career took place, however it is starting to feel like all those critics than one would find talking about PL/I and Algol 68.

The "C++" that is heavily lobbied at unis is still what I would call C+, with some exceptions at what are probably elite universities.

Just yesterday I was reading an ISO C++ paper that touches this issue, P2000R3 - Direction for ISO C++.

We need replacements, because no matter how modern C++ one might want to write in clean C++20, there are millions of lines of gone that go back to the pre-C++98 days, and plenty of mixed quality projects.

However, until Apple, Google, NVidia, AMD, Intel, Sony, Nintendo, Microsoft, ARM decide for something else on their SDKs, C++ is going to keep to be one of the tools to reach for on their platforms alongside the other platform languages, it is what it is.


The University where I studied didn't teach C++ to undergraduates, and as I understand it (I work there today, via a circuitous route, but not for the Computer Science department) it still doesn't. A friend teaches Haskell, and I believe their introduction course is in Java (mine was in ML).

I can't say that I'd think C++ would be a good choice. Perhaps someone who teaches C++ to undergraduates can offer their opinion about how that works out.

P2000 is an interesting document, and so thanks for causing me to read the current revision - but I'm not sure what you meant by "We need replacements". What is it that you want to replace? The C++ language? The C++ programmers?


It is clear that the way things are going, even the most passionate C++ developers can't make use of the language in a full safe way, understand the changes between revisions, and naturally Scott Meyers can't write books forever.

I certainly wouldn't manage a C++ interview quizz, even though I try to keep up with it.

The university where I studied during the 1990's, C was already on the way out.

First year students got Pascal and C++ with proper set of vector, string and other common data structures. This was a couple of years before C++98.

All data structures used bounds checking, and it was great, because the professor has a similar point of view as I, so C++ was taught as a kind of poor man's Ada in regards to safe coding.

What about C? Well, those taking OS classes were expected to be able to dive into it from C++ point of view.

However, this isn't normal, most institutes follow the more traditional path of C with classes, and most of them, hardly moved beyond C++98 anyway.


"naturally Scott Meyers can't write books forever"

He surely can't.

http://scottmeyers.blogspot.com/2015/12/good-to-go.html


Yes, I am aware of it. :)


> even the most passionate X developers can't make use of the language in a full safe way

Is pretty much true for any generally used language X. You can write security bugs, invalid domain logic, code that kills people etc. in any language that’s popular today. However there are examples of proven correct software (CompCert, CertOS, seL4 etc.) so we will hopefully get there eventually.


Scott Meyers stopped writing books a long time ago. You are out of touch, and no longer qualified to talk about how C++ is coded today.

There is no excuse for the malpractice of university departments. One could blame tenure, but the grad students assigned to teach don't have it.


I am pretty much in touch, contrary to you that argues for C++ no matter what, yet we seldom see any public work from you other than asserting how C++ is great and anything else is bad.

Apparently in your eagerness to once again argue for C++ in yet another C++ comment you translated "naturally Scott Meyers can't write books forever." as if I hasn't aware of the fact that he has left the community long time ago.

Apparently those words cannot have any other meaning as I am out of touch with what Scott Meyer does, otherwise how could you use it.

Anyone that bothers can assert many of my statements with public stuff from me on the Internet, they can even find naive Usenet stuff from me all the way back to 1994.

Prove us that your "expert C++ knowledge about the real market usage" is to be taken seriously and not polluting HN with C++ zealotry.


I learned it myself while at Uni because we only did C and Java.

I d say it s important to learn a bit just for the C with classes aspect. Sometimes you want to do a moderately complex C program that Java dev will have to help with, and it helps knowing C++ (as in: I did a 3D engine in it, using no overly fancy language feature, for me that s good enough to get an ability to produce something useful enough).

It's a bit sad Rust and Go are so far from C. Might as well just go to Java, a paradise for team work and problem solving.


Java is a paradise for managers who want to inflate their report head count. A whole team can be used up to do the work of one person.


I have seen certain Asian offshoring companies doing that with C++.

All the major ones you can think of.


Don't forget you have a complete market of C++ consultants and their companies that will do nothing but explain to you how partial template specialization works. Only for $$$$ per seat ;) Meyers, Alexandrescu, Sutter et al. built their careers by writing books and explaining arcane C++ details.


> built their careers by writing books and explaining arcane C++ details.

This is only true of Meyers. Alexandrescu and Sutter are accomplished software architects and have been working for large companies.


Sure, but is there any alternative to C++ if you are looking for an object-oriented language that compiles to native code?

Many modern languages don't have "proper" object-orientation, with subclasses, polymorphism, liskov substitution principle and so on. Rather, they have compile-time polymorphism, or opinionated other ways of doing it (composition in Go, traits in Rust. Methods in Nim are wierdly attached to the language, and if you want interfaces, the author recommends you use a closure and function pointers...)

Other "classic" object-oriented languages are all managed (C#, Java).

What I'd want from a language is:

- a powerful but ergonomic type system (stuff like Nims "distinct" types are nice). Maybe we can even get simple dependent types.

- Class-based object orientation is nice for 1) writing UI code and 2) getting rid of duplicated ifs everywhere.

- Generics. Bonus if we can control contra/co/invariance of type arguments. (A Ptr<ToggleButton> is a Ptr<Button>, but a List<ToggleButton> might not be a good List<Button> everywhere, since you can add ToolbarButtons for example.)

- Functional programming: Functions and methods as first-class-citizens, built-in understanding of pure functions and const data

- Async programming: async/await as first class citizen

- Compiles to native code

- No frickin UB.


You probably know this, but for reference, while Rust doesn’t support implementation inheritance, it has inheritance and runtime polymorphism at the interface level.

You put your dynamic components into references and smart pointers of the form Box<dyn Component>, where Component is a so-called “object-safe trait” (which is exactly what you’d call an interface in the OO world). These traits can inherit from each other, and you can upcast a Box<ConcreteType> into a Box<dyn Interface> into a Box<dyn ParentInterface>.


No I don't know Rust very well, I really should put in some time to learn it properly! I did a bit of GUI programming in the past, and felt there is a bit of impedance mismatch with Rust there. And the kind of subtle mistakes with ownership and reference cycles you tend to make in C++, but discover when you move to unique_ptr: Rust also ruthlessly uncovers them, but I guess I need more experience how to solve them.


> Other "classic" object-oriented languages are all managed (C#, Java).

True, but there have been always ways to compile them to native code, granted not all of them as free beer.


That is asking for quite a lot. "Proper" OOP _and_ proper functional programming in one language...


Nah, I'll settle for good enough functional programming (map, apply, nice closures). Python, Kotlin, recent C#, JS/TS all have this.


Pretty sure C# had this since about 2008 with LINQ and lambdas in C# 3.


Swift? (minus the ability to specify co/contra-variance)


Probably a dialect either of Pascal or BASIC.


Ada covers quite a bit of what you want.


> It's also hard to know anything other than C++, because of how taxing the language is; there's little energy left in you to learn something else.

When I think that in my industry, good mastership of C++ is, what, 30% of what it takes... Throw in real-time design principles and a few different kernels, several CPU architectures, unavoidable Linux and a one or two build systems (Yocto anyone?). And only then comes the real problem domain, communications protocol stacks or image-processing algos, or what have you.


Fully agree that the language is very taxing on the braincells and I think this is the main reason why Senior C++ Wizards tend to hate Rust. Imagine spending 10 years learning the little details about lifetimes, thread safety, million different UB cases etc. and then seeing a kid fresh out of college fearlessly writing performant code by leveraging the amazing Rust compiler. I bet it must be an infuriating feeling which is why I'm glad that I bailed out on C++ early in my carreer.


> seeing a kid fresh out of college fearlessly writing performant code by leveraging the amazing Rust compiler

A nice (and very ridiculous) fantasy, but no.

As a person who hires developers to write performant code, I'd love it if there were these magical junior Rust developers who can write performant code.

But as the market exists right now, Rust developers are twice as expensive and the product they deliver is twice as bad.

The only way I can justify Rust to higher management is selling it as a vanity PR project.


The main point of Rust is correctness, not performance. Performant code is a nice to have - but when the two are in conflict, correct code wins every time. How can one ever be confident that your Senior C++ devs are writing code that is free of subtle bugs? Are you going to look for professional Haskell or Ocaml devs too, for a proper comparison? Those folks ain't cheap, either.


I maintain large scale C++ code that hasn’t had a bug in production for 5+ years. I do it using lots of system level tests. So it is very much doable. It has nothing to do with the programming language. Yes some languages (Haskell for example) completely eliminate a large class of bugs, and I would prefer to use languages like that, however the world runs on C/C++. So having solid C++ skills is great for your career.


> But as the market exists right now, Rust developers are twice as expensive and the product they deliver is twice as bad.

What? Why? I've been developing C++ for 10 years and seen firsthand the problems it causes and that are addressed by Rust. If I start writing Rust I'm going to be more productive (from build system to maintenance), write more performant and reliable code faster, so I don't see how the delivered product would be worse, as I'd have more time to iterate.

About the rate, maybe that would be more expensive, but I'd say it's still worth it. Anyway, it's not like getting competent C++ developers is cheap either.

As for GP's message, I agree it looks unrealistic that "kid fresh out of college" would magically write performant code because they're writing rust. There is more to performance than the language, and it is particularly easy to write bad performance code in rust (and in C++ I must add). That being said, I'd still be less anxious to have a junior in a rust codebase than near a C++ one.


> If I start writing Rust I'm going to be more productive (from build system to maintenance), write more performant and reliable code faster, so I don't see how the delivered product would be worse, as I'd have more time to iterate.

Maybe it's true for you, but I can hire a competent C++ programmer to deliver code faster for less than what you'd ask.

Also, if a programmer is spending time "iterating" on a performance problem, he's already underperforming. That's okay for less experienced developers as part of the learning process, but expecting it to be the norm? No.

> About the rate, maybe that would be more expensive, but I'd say it's still worth it.

Real-world metrics do not agree.


> Also, if a programmer is spending time "iterating" on a performance problem, he's already underperforming. That's okay for less experienced developers as part of the learning process, but expecting it to be the norm? No.

I don't think the OP meant iterating on performance problems specifically, just generally iterating on code as one does when they write it.

Also, every programmer at some point iterates about performance problems, not matter their experience, or language (or implementation), or runtime that's being used.

I think the original "uni grad can write better Rust code than experience C++ dev can write C++" is a hyperbole. I don't think they meant better, but the Rust developer will need less context to implement the same feature.

The spirit of that sentence was probably "it's simpler to write performant and safe code in Rust than C++". But again, without really agreeing what "performance" means here, it's a bit silly to discuss.

Performance is such a complex that can mean so many things depending on the problem. So, I we're all nit-picking about something without even agreeing what they're specifically nit-picking about.

The default IO mode in C/C++ is buffered. Rust requires you to wrap it in `BufWriter`. For most network (or general IO, really) workloads you want buffered IO.

So sure: We could say that writing performant IO code in Rust is more difficult, if we hyper-focus on this one specific detail.

FWIW (and people who're unfamiliar with this): I think switching to buffered IO in Rust is absolutely trivial, and very well documented (in multiple places where it's relevant). In fact more so than it is to switch to unbuffered IO in C++, because you have to call `setbuf` with "special" values to disable buffering.

We can then argue that experience/wisdom diminishes most of these arguments about performance, but it diminishes them on both sides.


> I don't think the OP meant iterating on performance problems specifically, just generally iterating on code as one does when they write it. Also, every programmer at some point iterates about performance problems, not matter their experience, or language (or implementation), or runtime that's being used.

Exactly, on both accounts, thanks.

It strikes me as odd that GP would describe "iterating on performance" as "underperforming", unless GP is in a very specific domain where the performance requirements are always known in advance and where they never change. Getting the last drop of performance from code is a trade-off. It impacts the readability and maintainability of code. To get the last drop, it can depend on the shape of your expected input data, and it requires looking at the produced assembly and performance profiling results to find bottlenecks and act on them. This alone implies iteration. If you're telling me that your engineers know their compiler so well that they can tell in advance what assembly code will be generated when changing seemingly unrelated parts of the code, eg they know the inlining limits of their compiler, I call BS.

Iterating on performance takes time to develop and maintain, which is why it should be done just enough to meet the requirements, in particular in the hot loop and to reduce bottlenecks. If your developers are optimizing all the code they write to the ASM instruction, they are probably underperforming (unless specific domain where so little code is written or the target is so small that the benefit is worth the cost). Understanding that trade-off is part of seniority (aka juniors will often both "prematurely pessimize" by writing inefficient code without increasing maintenability or in the hot loop and "prematurely optimize" by avoiding allocation at all costs in CLI argument parsing).


[flagged]


It is a big part of any language promotion. I remember some Ruby guy claiming that developers are 100 times more productive in Ruby. Yeah right.


It feels like at least 100x if you are trying to scrape a website and do stuff with the data, and you are comparing C and Ruby...


And it feels like 0.0001% if you want to write a fast device driver, game, or anything else performant.


I measured it. On multiple projects. Some I started writing in Rust, so this is not the "rewrite effect" at play. I observe x2 to x3 on development time alone, long term if we compound review, debugging and maintenance I wouldn't be surprised to see if it tends to an order of magnitude of difference.

Results will depend on the available ecosystem for each language for the task at hand. For instance I'd still choose C++ with qt for writing a GUI today.

The reasons why Rust is more productive are easy to understand and touch most of the development steps:

- building is trivial in Rust, adding dependencies also. Depending on how broken the CMake of your C++ dependency is, it can take several days to wrap it so it can be used sanely. The remaining time is dependency review, which must also be taken in C++.

- Development itself is much easier, thanks to Rust's expressive typesystem (mostly sum types and traits) and "functional-lite" iterators (C++ is getting there with ranges but today we're still far from the same ergonomy, we're still missing pattern matching, and views introduce lifetime issues). The sane error/warning demarcation allow to develop without the equivalent of "-WError" (which is hard to do sanely in C++ due to eg missing return being a warning), which allows to use these warnings to drive development (you can stub every function and when you no longer have any "unused variable/unused function" warning, you're done). More generally, Rust removes most of the boilerplate of C++: you don't need to maintain superfluous representations of your interface as headers (the documented interfaces are generated using cargo doc), you can derive `Hash` instead of opening the std namespace, functions and structs definition all start with a keyword so they're easy to grep for, the language being expression oriented means we don't have to rely on immediately invoked lambdas as soon as what we want is to bind a variable depending on an if, we don't have pure stupid boilerplate like the rule of five to enforce, and a thousand of other ergonomic improvements over C++. Rust feels like a language designed by a C++ developer tired of the flaws of C++.

- Since many errors are lifted at compile time, the feedback loop becomes much shorter. On the same library that has both a C++ and a Rust interface, we had memory errors several times due to a function that returns a view of its argument without that being clearly expressed or expected. In C++, this meant looking for what went wrong after a segfault, something that took an hour to two engineers. In Rust, the compiler told us that the particular variable was not living long enough, and went so far as to suggest the fix. Time spent on this: 10 seconds. And this comparison only hold for the memory errors that we did catch in C++...

- Test and documentation use standard tooling that is included by default. The ecosystem documentation is stored on a centralized website, meaning is a single place to look at to find documentation (that at least contains the interface of the library). - Review is made easier with no implicit code paths due to implicit conversions or exceptions. Error handling has the right amount of noise, a single character to pass back the error (possibly adding some context), otherwise a match construct to handle each error case. The error story is not perfect, but much better than in C++. The "dangerous patterns" are made easy to spot (unsafe, unwrap, etc) and should be confined to specific parts of the code (eg hot loops where benchmarks prove that it is necessary). Rust make code review able to focus on the problem at hand rather than the many rules you have to follow to not blow your foot off in C++. In the presence of other developers (or even for myself :-)), it is really welcome.

- Maintenance time is reduced by all of the above, over time, compounded by the ease with which one can refactor Rust code compared to C++ code. If refactoring a module without unsafe there is no chance of introducing memory issues. Generally the strong type system (no implicit conversions) makes it easier to not accidentally introduce logic errors

Now, there are drawbacks to Rust: the ease of adding dependencies means that we can have deeper dependency tree, so this make reviewing dependencies a bit more tedious (thankfully code review is easier). Occasionally the borrow checker comes in and forbid some patterns (mostly when we would write code depending on iterators not being invalidated in C++), and having done less rust than C++ sometimes I need to find the correct pattern for something. On average though, the advantage is very clear.


Which is it? Barely a day ago, you said "If I start writing Rust..." Now you claim you have been writing Rust all along, on "multiple projects", keeping measurements. Forgive my skepticism.


Sorry for not being clear. Should have written "when I start writing Rust" at this point. I have been using Rust personally since 2016, and professionally since 2019. Most of our codebase is naturally still in C++ (we're not aiming at "rewriting the world"), so I can frequently feel the difference between the two languages, which is why my preference is so clear-cut.


> But as the market exists right now, Rust developers are twice as expensive and the product they deliver is twice as bad.

It's interesting how we have different experiences regarding this. In my experience, it's usually the opposite. And I believe it's mostly because Rust lets you focus more on the problem you're solving, rather than thinking about dos and don'ts of the language.

For example, most Rust projects (or products) seem to have much more polish than equivalent C++ projects (even for ones with years head start). By polish I mean both user experience and the developer experience when one works on the project.

Also, adding user-facing (i.e. UX) polish to Rust projects requires less ahead-of-time planning in my experience.

One reason is that when introducing new Rust code, there's less performance gotchas, and Rust code seems to compose more ergonomically than C++ code... Most of the time at least; there were times that Rust was frustrating. But I think less often probably, _and_ the frustration is compile-time, rather than runtime. Also Rust has a shorter feedback loop due to Rust's incremental compilation really helps mitigate the frustration, so you can iterate faster.

And another is because I've always felt like refactoring Rust code (even with C++ experience than Rust) tends to be easier, and faster to refactor, and I feel bolder while doing it, if that makes sense? Simply because the compiler thinks about various categories of problem domains instead of me, and it tells me about them.

Of course, it's never black and white with any of the above things I mentioned; the situation is grey in both ecosystems. But I think it's a much lighter shade of grey in the Rust space than in the C++ space. And as always, there's always exceptions on either side, too.

EDIT: I quoted OP's full sentence, but then only addressed the last bit. I hopefully clarified in comments below that I wasn't talking about the first part. Sorry about that!


> In my experience, it's usually the opposite.

Obviously your experience doesn't involve budgeting or hiring developers.

Your subjective feelings about how nice or fast it is to write Rust code isn't what we're talking about here.

The original poster claimed that Rust can save money by letting managers hire junior devs to replace senior C++ developers.

This isn't true. Rust developers are much more expensive than C++ developers, and have vastly lower productivity. (No doubt because of their vastly inflated expectations; any Go/Python/PHP developer who upgrades to Rust suddenly thinks they are elite 10x programmers because they have a clue about performance now.)


> The original poster claimed that Rust can save money by letting managers hire junior devs to replace senior C++ developers.

This is not at all what I said, nor meant. I never said anything about the hiring market, but merely stated a personal observation why I think Senior C++ devs often dislike Rust.


You were wrong about that, too:

Senior C++ developers are not impressed with Rust because it doesn't solve any problem they have. Switching to Rust would make them markedly less productive, with no corresponding benefit. The drain comes from slow compilation and from excess design overhead, the need to distort a simpler architecture to satisfy the borrow checker.

Put simply, the extra work to satisfy the borrow checker prevents errors they do not make.

This contradicts Rust propaganda, where a few CVE numbers are cited over and over, echo-chamber style. What these citations never mention is that they are for bugs in mostly very old code. We just don't need to write code that way anymore. It is not just error-prone, it is more work, so there is simply no temptation to write it that way.

I do hear of programmers who have elected to stop learning, and (proudly!) admit continuing to write C++98 or even "C-like C++". We used to say "you can write FORTRAN in any language"; there is no eliminating bad programmers, but plenty of reasons to avoid their product. What conclusions can we make about Rust from its worst adherents?


> What these citations never mention is that they are for bugs in mostly very old code.

It is unavoidably true that grave security holes get found only in existing code and not hypothetical future code written with a later language revision and whatever new features will definitely avoid those "errors they do not make". That's time's arrow for you.

Can you name a few of these "Senior C++ developers" and point us to, say, a few years of their work?

Is there a specific vintage of C++ where you feel like, at last, unlike C++ 98 you "don't need to write code that way" and so Senior C++ developers won't make these errors? C++ 11 maybe?


> Put simply, the extra work to satisfy the borrow checker prevents errors they do not make.

> This contradicts Rust propaganda, where a few CVE numbers are cited over and over, echo-chamber style. What these citations never mention is that they are for bugs in mostly very old code.

Empirically this is not true. https://twitter.com/LazyFishBarrel/ is not citing "a few CVE numbers over and over"; for example, in browsers, new code is disproportionately responsible for vulnerabilities. I know most of the people at the big tech companies doing the research into where the security problems in major C++ software come from; they're not making the results up.


When I look into these CVEs and see "use after free", I know immediately it is not a problem in modern code.


> it doesn't solve any problem they have

you mean a problem blithely ignored until the software becomes important enough to attack, at which point, it's too late to rewrite the "very old" code? https://googleprojectzero.blogspot.com/2022/01/zooming-in-on...

> errors they do not make.

bullshit. a quick look at oss-fuzz on c++ projects, many of them in modern c++, will show that they are teeming with memory unsafety.

> there is no eliminating bad programmers, but plenty of reasons to avoid their product.

on an unrelated note, what product are you responsible for, so i can avoid it?


There is no possibility you could ever afford it.


the world can't afford new code written in a blatantly memory-unsafe language


> Obviously your experience doesn't involve budgeting or hiring developers.

Ah, that is very true! :) I forgot to make clear in my comment that I wasn't referring to that bit at all.

I only participated in the hiring/interview process, but that's as far as my hiring experience goes.

> The original poster claimed that Rust can save money by letting managers hire junior devs to replace senior C++ developers.

Ah, I didn't interpret it that way. I thought the point they wanted to get across was that it's easier to get the same for less—not in terms of monetary cost, but in terms of less mental effort with regards to context/knowledge about the language itself.

But, the hiring point you're addressing can be inferred from their comment, too.

I think I agree with you on that... It probably is "easier" to hire C++ developers, since there's an older and bigger market for them.

I think Rust developers are in an interesting position right now that they're not as abundant as other developers, since Rust is probably still in "early adoption" stage. So they can use that as leverage in salary negotiations, maybe? But it also comes at a cost of a much smaller job market for them, too.


> I think Rust developers are in an interesting position right now that they're not as abundant as other developers, since Rust is probably still in "early adoption" stage. So they can use that as leverage in salary negotiations, maybe? But it also comes at a cost of a much smaller job market for them, too.

No, you are viewing that from a technological perspective - what you assume only holds if "Rust" was a requirement.

If staffing a project is much harder (and maybe more expensive) when using Rust as language, then Rust will not be chosen if C++ is an option.


I'm not sure about that. I've spent 18 years programming in C++, for much of that time in a senior professional capacity and am excited to learn Rust for whatever my next project will be. Most of my colleagues and peers feel the same. I think the person you are characterising is quite rare. I like C++ but don't love all of the baggage and am delighted that things are moving forward.


> I think the person you are characterising is quite rare.

You could be right... I hope you are right, but I'm afraid you're not...

> I'm not sure about that. I've spent 18 years programming in C++, for much of that time in a senior professional capacity and am excited to learn Rust for whatever my next project will be. Most of my colleagues and peers feel the same. [...] I like C++ but don't love all of the baggage and am delighted that things are moving forward.

I don't know what the "split" is regarding mentality, and I've definitely seen a lot of experienced C++ devs in Rust communities, but then there's also a lot of people in "Rust bad"[1] circles (you see them on Reddit and HN). I think it might be an even split (as with most polarised opinions), but I don't think that either sort is rate.

[1]: I don't blame these people, btw. If you're in a senior or leadership position and you choose C++ for a new project, because C++ is your strong suit. Then you read online "C++ bad" comments that glorify Rust. It's hard to not take these things personally, when it might be affecting your livelihood. Especially if semi-technical management "drinks the Rust Kool-Aid" (for a lack of better expression) and grills you over your decision. It's not a very disheartening situation to be in, and that frustration can fester into contempt, and so on.


I think maybe you're paying too much heed to what you see in concentrated and very self selecting online communities. A lot of professional C++ programmers are more focussed on particular industries, and the domain problems to solve and less so on the language or specific tools. It just so happens that C++ has been the only game in town in a lot of industries until quite recently. At work we've been looking into trying out Rust in some modules where it would make sense. There has been zero objection based on it being Rust, from a team of senior C++ devs. Quite the opposite, most are keen to try it out. But those people don't spent their time on language message boards. Sure, you will find the type of person that you describe. They definitely exist. But it's not the typical case of senior C++ developer, in my experience. Most of us are domain experts in something (automotive, security, medical devices, games, OSes, networking, etc...) and the language comes secondary. If we can better solve our primary goals with a different language then we'd be fools not to have an open mind.


Senior C++ developer here and I totally agree. Anything that can improve productivity/quality while maintaining high performance is very much welcome. Obviously!


> I think maybe you're paying too much heed to what you see in concentrated and very self selecting online communities.

That's a good point. In the domain specific communities, I've not not really seen language flamingo.


In my experience, the problem with juniors and performant code has little to do with lifetimes. The issues I see are as easy in C#, Java, or Rust as C++ - e.g. repeatedly using string appends rather than building up a list and doing a single allocation, or holding a lock for the duration of a reload and parse instead of just the reference swap, etc. Thread safety, more often than not, isn't that hard - just do the work on one thread (I work on a web service with thread-per-request).

I'm optimistic about Rust but realistic (I think) about how much inertia the common project (C++) really has. I'm also decidedly skeptical about Rust's cure-all possibilities - while I've seen a dozen or so real lifetime bugs in a decade, about half of them were caused by a developer misusing an API, deliberately calling a method that is for transferring lifetimes, and then not assigning the reference to a new object.

In my experience, most bugs are just badly written code, not the sort of problem your compiler can fix.


> repeatedly using string appends rather than building up a list and doing a single allocation

I think this "make a list" idea performs best where you know exactly how many things will be in the "list" but have little idea how long the concatenated String might be, so you can keep the "list" as a local array (no allocation) and do a single String::with_capacity (one allocation) and then push all the references onto it.

Otherwise, obviously we're not going to go around assembling a LinkedList since this is the 21st century and we're not crazy, so let's assume if we don't know how long that "list" is we're accumulating references in a Vec<&str>. We don't know how big to make the Vec either, and so that will grow as we push items onto it.

Rust's Strings are - under the hood - basically a Vec<u8> that promises to be valid UTF-8. So we're playing with the exact same allocation algorithm and same amortized costs in String and Vec<&str>.

If we're appending very few Strings in the typical case, the "list" approach costs extra because we're doing a bunch more allocations + copies to make the Vec<&str> that we're throwing away shortly afterwards. Likewise if the strings are very short.

And after all that it's more complicated, which counts against it unless performance was critical.

If I saw a junior reflexively doing this (with a Vec or some other "list" structure) in code I was reviewing, I'd ask them why. If their answer is "performance" I'd remind them: If we aren't measuring it then it isn't performance it's just wanking. So, where's the measurement?


Depends on your string really. If your string are immutable (as in C#), it's not the same amortized cost and you end up wanting to use a StringBuilder almost every time. It's not premature optimization - it's just the standard pattern for performant coding.

But I see your point - I inadvertently gave an example that is performance basics in some languages and fine tuning in others.

The lock example is perhaps more relevant.

Another good example is appropriate caching. Often services want to parse something (e.g. an ini file) and cache it in memory. Caching the right (fully parsed) format will lead to a highly performant configuration system - taking less than 1% of CPU time - but let some juniors do parsing on top of the caching layer rather than underneath your caching layer - and suddenly your config system is 5-10% or your service CPU and is a target for optimization. Most of my daily work in growing juniors is patterns like this, nothing a language would help with.


Here is another example, using a for loop instead of an intrinsic like System.arrayCopy().


They hate everything that takes 1us from them, following on the C culture.

The nice C++ frameworks from the 90's all used bounds checking enabled by default, what does ISO C++? Bounds checking disabled by default and it is up to the compiler vendors to do as they please, hence FORTIFY and _ITERATOR_DEBUG_LEVEL.

If you read how magnificent C++ is on the HILO tutorial or C++/WinRT ones, it is quite clear how WinDev sees these nasty folks doing stuff in .NET at DevDiv.

Joe Duffy on his Midori talk at RustConf mentions that even with Midori being available to WinDev they were asserting it wasn't possible.


“Wizard” is the right way to put it. There’s a certain kind of “experienced” developer who enjoys being the local guru way too much. Even regardless of technology, their projects are invariably set up in such a way that no one can do anything without them.

C++ seems perfect for these people because there are so many little traps and arbitrary rules to remember. “Ha ha, you wrote for (const auto x: y) instead of for (const auto& x: y)? Stupid fool!”

(Or is it auto&& now? decltype(auto)? I honestly don’t remember and don’t really care.)

I’m not remotely suggesting all or even most C++ experts are like this, but anecdotally, their share is higher than among, say, Python programmers.

It’s not like Rust is particularly easy to learn. On a conceptual level, it’s probably harder than C++ (traits, generics, lifetimes). It’s just so much less arcane.


Both are wrong, east-const is the one true way!


I sympathise with East Const, but immediately I am just reminded of another problem with C++

The defaults are wrong. Const should have been the default. Whereupon "East Const" doesn't matter, all the variables which are mutated get labelled. Similarly "explicit" should be the default (nobody wants your random single parameter constructor to be implicitly called, stop that) and "override" shouldn't be (If I name my method foo why is it my responsibility to first check whether some other bozo named a method foo so that I don't "override" it ? The compiler can and should tell me this won't end well†)

† Rust brought me around to the idea that some other bozo's quack() method has no business sharing a namespace with mine just because we're both Ducks, if I wanted to override the shared method I'd have re-implemented Duck::quack() not added my own quack() method. But in the C++ world I'll stick to just saying override shouldn't silently be the default.


> The defaults are wrong. Const should have been the default.

yeah no.

    struct foo { 
      int x;
    };
would not be compatible with C because the int would be const, that would have made the language particularly useless.


Right, C++ has CV-qualified member variables†, because of course it does.

I'm not talking about those. I think they plain shouldn't exist, but it's a separate discussion from the discussion about the defaults.

But yes, this means you can't cut-and-paste C code. I appreciate that this is disappointing for the "Actually we already have lots of C++, it's just that it's mostly .c files and doesn't realise it's C++ yet" people. Set your sights higher.

† Yes, this insanity is also available in volatile flavour.


Not having that would mean no access to the Windows SDK, no access to a libc, the macOS frameworks, etc. It's far from negligible. It takes other languages years to wrap the OS APIs correctly, if they even do it.

Why would CV members be insane ? Having const members has been very useful for me when refactoring codebases, to make sure that I knew every place modifying such a member. It has never been a hindrance, just plain positive, and not having it would mean one more rule to remember (and having to explain students why you can write "const int foo;" here and not there)


Uninitialized constant foo

They actually can't (well, shouldn't) write "const int foo;" as a local variable because the compiler needs to know its value. And that gives the game away.

These are already different cases, yes both the structure member and the local variable are names with types, but they already need to be explained separately.


I have been programming in C++ for a very long time (25+ years career). And no I don’t hate Rust at all. I welcome any attempt to make better, safer language that are still fast. I actually wish that Rust had gone one step further and implemented Dependent Types and Refinement Types. It would allow you to prove code correct in Rust. Which is even better than the current Rust guarantees. Having said that, there are about 5 million C++ users out there writing C++ software every day. So it will take a loooooooooong time before Rust usage catches up. If it ever will.


I defended C++ as well, because - even if it's ugly and error prone - I felt like you can express almost any in C++ while every other language felt pretty limited, and from C++11 on and research from Herb Sutter, I thought it would only be a matter of time until C++ would become a actually a modern & safe language.

Well, still waiting for that...


The best C++ developers are those who violently oppose extending the subset of C++ features used in a project.

"Template specialization and traits? This belongs in library code. The resulting code is actually more bloated than the copy-pasted version. Nope."


"The resulting code is actually more bloated than the copy-pasted version"

How is that possible?


Templates expand, and can generate arbitrary amounts of code. If you, as a developer, don't see what happens, then you might miss it.

Templates can also expand on each type they use, or combination of types, generating much more code, when non-template solutions could have avoided it. Of course, templates could avoid it too, but they're much harder to write and see how they expand.

That said, templates also allow making code shorter and cleaner.

So some people, once bitten by bad templates or bad usage, complain without learning how to use them. Yet another C++ ocean of complexity :)


It is possible with every kind of de-duplicating copy-pasted code. You need some boilerplate for the common template (not necessarily in the C++ sense), then some more boilerplate for its instantiations to replace the previously-copypasted blocks of code.

Then the blocks weren't exactly identical, so the template (again, not necessarily C++) needs to gain placeholders/ifs and the instantiations need to fill them/provide conditions.

If you do it with C++ templates using the traits machinery, it generates quite a lot of bloat. It is not a reason to never use these mechanisms, just to be wary of the costs.


"If you do it with C++ templates using the traits machinery, it generates quite a lot of bloat. "

That's exactly the part that I don't understand. How does using traits machinery generate more bloat than some other way to de-duplicate the same code? And what kind of bloat do you have in mind -- source code or binary?


More bloated != more lines of code


Unless you mean 'More complicated != more lines of code' I don't understand you.


Been a C/C++ daily user for 40+ years. The key to being great is to ignore much of what is possible in C++'s templating and just write C code, and only borrowing C++'s highest level OOP class structures. One gains the organization of OOP and faces none of the template nonsense "mature" C++ developers get hung up trying to achieve. Template only when no other method makes sense, and do so cautiously. Why? Because template metaprogramming is simply too error prone, and the time to debug that is less than the hassle of doing the same without templates.


It is easy to find people like bsenftner who have elected to stop learning, and to continue writing in an old, crappy, error-prone style. They are not representative of competent C++ coders.

For the record, OOP is a bad fit for most problems. The overwhelming majority of actual template use has nothing to do with "template metaprogramming", which has been superseded by modern language features he evidently knows nothing about.


Easy cheap shot. Who says anyone stopped learning? Coding is not the goal, which the vast majority of developers seem to not realize. The goal is the minimal technology necessary to impact the physical world here. Lay down the latest buzz words, pick up the known tools and get the job done. Look past the technology and critically assess how to impact the world. You may see "old, crappy error prone style" while the greybeards see familiarity and understand what is happening without mental gymnastics, understand the code base in a fraction of the time, and deliver evergreen, fault tolerance processes with exponentially less expense to maintain and operate. While you're busy writing the latest resume jerk off, I'm implementing industry leading enterprise applications and acquiring global patents.


I am a greybeard who has not abandoned learning new things.

Modern features in C++ are not there to pad resumes. Each is carefully selected from a much larger number of proposals as demonstrating it enables writing cleaner, clearer, more reliable, faster code. If you choose not to use any of them, you are perforce choosing to write less clean, less clear, less reliable, slower code.


What is this aggressive "abandoning learning new things" label? My teams choose a method of C++ that does not include deep hierarchies and shuns generic programming via overuse of templates. Beyond that, we focus on the purpose of the software, as it why are we here and how do we solve this within a sustainable and minimal expense frame. As far as "less clean, less clear, less reliable, slower code" - you are a complete and utter fool to believe such fantasies. Those points are why I and my teams write code as we do: it is clean, clearer, more reliable, and exponentially faster.

And the sheer audacity to think only your method produces such results. Grow up.


That you mention "deep hierarchies" as something you evidently imagine must exist in others' code reveals all.


You project a lot.


This kind of restraint and level-headedness is the most important trait of a senior developer. The industry sorely needs more of them, not just in C++…


> If you wanted readability, or maintainability you have to sacrifice performance.

I would say that, at least as of 2011, this is not true. However - I would posit a 3-parameter tradeoff:

1. Better Readability-and-maintainability 2. Better Performance 3. Less coding effort

If you want (1.)+(2.) you won't get (3.), i.e. you'll have to work hard.

You may be able to write performant code relatively quickly, but it will tend to be more difficult to read and maintain; and you can write readable, maintainable code relatively quickly, but it may not squeeze out what it can in terms of performance.


I admit that I did over-generalise with those two statements. There's different tradeoffs one needs to do in any language regarding performance/maintenance/effort.

But for C++ there is little effort to bridge those gaps, or at least it feels that way. Maybe that's unfair of me, because C++ is very keen on backwards compatibility, though; I disagree with how religious they are about that goal, too.

Rust on the other hand strives to make idiomatic and easy-to-maintain/write/read code perform well. There are obviously high-level decision that play a role in performance, but I'm talking more about "vocabulary features" of the language/ecosystem, not the highlevel decision of "Do I use a List, a Map, or a Vector for this?"

Sure, there's ugly/unsafe and hard-to-understand parts in Rust libraries (and stdlib) too, but those warts don't leak into the "consumer" code (at least not as much). Also there's consistent effort to add DX polish to tooling, that's not such a "cultural thing" in the C++ ecosystem


There's some small effort to land quality-of-life improvements to the C++ standard library but it feels like too-little too-late.

As an example, for years C++ didn't have std::string::contains() because you could write find(x) != npos -- and so the argument is why carry around this unnecessary function? Well, because that's what programmers often actually wanted. C++ 23 will finally get std::string::contains()


> As an example, for years C++ didn't have std::string::contains() because you could write find(x) != npos

Yes, because you would have a utility header where you implemented it like so:

    bool contains(const std::string& s, char c) noexcept 
    { return s.find(c) == std::string::npos; }
> and so the argument is why carry around this unnecessary function? Well, because that's what programmers often actually wanted.

No, we didn't want the method, we wanted the function. Actually, string has way too many methods, most being useless, in the sense that they are trivial to implement as freestanding functions.

But really, what programmers (should) actually want is uniform function call syntax: https://en.wikipedia.org/wiki/Uniform_Function_Call_Syntax - which would let us blur the distinction between freestanding functions and methods.


> If you wanted readability, or maintainability you have to sacrifice performance. If you want performance, you have to sacrifice everything else.

This is true in every programming language to some extent. What makes it worse in C++ is that the language markets itself as being performant without any trade offs (zero cost abstractions!) and the reality is very different from that.


> (zero cost abstractions!)

You probably mean zero overhead abstractions. The idea is that you can't outperform it by handwritten equivalent code.


The name is irrelevant, the point is that it's a blatant lie.


Favorite excerpts:

   I know C++ better than it deserves
and

   Finally, the following sentence is only here to attract a certain set of search 
   engine users interested in our subject, and should not be interpreted as the 
   summary of my point of view on the complicated issues discussed:

   C++ SUCKS


Past related threads:

C++ Frequently Questioned Answers - https://news.ycombinator.com/item?id=887722 - Oct 2009 (28 comments)

C++ Frequently Questioned Answers - https://news.ycombinator.com/item?id=602981 - May 2009 (73 comments)


I would like to comment on the claim that "all popular languages have context-free grammars, while C++ has undecidable grammar". Although I understand what is meant here, the terminology is just plain wrong. There are no such things as undecidable grammars. There are only undecidable problems. The C++ grammar is context-free, period. What gets people confused about it is that it is ambiguous, and that there are in fact two parsing problems you can associate to an ambiguous context-free grammar. One of them is decidable, the other is not in the case of C++.

The first parsing problem is what I would call textbook parsing, as this is what is usually presented in books on the topic. It is a decision problem: the answer is either "yes" or "no". For context-free grammars, including C++, it is always decidable. So what about the ambiguous `AA BB(CC);` given in example here? Is it an object definition or a function declaration? The (decidable) textbook answer is simply "yes". The key thing to understand here is that textbook parsing does not require resolving any ambiguities. The "context-free" in "context-free grammar" only refers to this decision problem, which is why C++ has a context-free grammar, because textbook parsing does not require looking at any surrounding context.

Of course, if you are writing a compiler, "yes" is not a very useful answer. You want either "yes, it is an object definition" or "yes, it is a function declaration", which turns the parsing problem into a completely different one, which I will call compiler parsing. Compiler parsing is not a decision problem, and in the case of C++, it is undecidable.

What is meant by "all popular languages have context-free grammars" is simply that most languages have an unambiguous context-free grammar. To add to the confusion, textbook parsing and compiler parsing are essentially the same problem for unambiguous grammars.


I think they’ve gotten remarkably far with the language but they still desperately need to build an “opinionated subset” (of both the language and the STL) so that new generations have a hope of learning it properly. Perhaps modules will make it easier to do so in a way that allows interoperable code.

I would love to see a backward-compatibility compiler and library that is essentially whatever C++ is now, frozen in time: any “.cpp”/“.cc” file is simply built with that. All new C++25 code should be something else (maybe “.ocp” for Opinionated C++?).

The “.ocp” files would build against a complete replacement for the compiler and standard library, offering ONLY the sane subset of the original language and library that we have always needed. Finally remove all these constructs that we are not supposed to be using anymore. Throw std::iostream into a dumpster and set it on fire. Refactor all of this code that had to be implemented in weird ways simply because of the desire for perfect backward-compatibility. And finally take the opportunity to have sane defaults, which alone will probably allow 12 different keywords to be stripped out of the evolved language.


Building an "opinionated subset" of C++ is definitely non-trivial - the C++ Core Guidelines are the closest thing to that (with plenty of input from standard committee members) and they're still far less usable than an actual greenfield effort like Rust.


Yosefk had one of my favourite programming blogs ever, sad he no longer updates.


He still tweets regularly, I find his tweets very funny sometimes. https://nitter.net/YossiKreinin

https://nitter.net/YossiKreinin


> If you end up working with C++, don't try to "fix" it (or "boost" it). You'll just add more layers of complexity. The most productive approach is to accept the problems and try to write simple code which people can easily follow.

I don't think this is specifically a C++ problem. It's true of any language, especially in a team setting.

Reminds me of the post from yesterday, where some people where adding decorators in python to 'unmutate the mutable default argument' instead of just setting it to 'None'.


[33.1] Is the type of "pointer-to-member-function" different from "pointer-to-function"?

It’s interesting to notice how “this” is what distinguishes the two.

When creating member functions in C, function pointers are assigned members. In order to access or change the object with an assigned function pointer, that object needs to be called or in other words, “this”.

obj.member_function(&obj);

In C++ would be

obj.member_function();


People actually put functions in structs in C to make it look like OOP!? That's horrifying!

Or am I (hopefully) misunderstanding something here?


I'm not sure how common it is in practice. CPython and GTK are the most popular examples of OOP in C that I can think of.

It's pretty pointless unless you want some sort of namespace-like organization. Maybe for polymorphism because you can change an instance's "base" member function pointer.


C++ certainly has flaws - but it also has intentional design choices, which are important for achieving its goals. Also, as others have mentioned - much has improved since 2009.

To make this concrete, Let's go over the "C++ defects" summary appearing at the link, before the individual "frequently-questioned answers".

> changing the private members of a class requires recompilation of the code using the class. When the class is used to instantiate member objects of other classes, the rule is of course applied recursively.

This is mandated by two of C++' language design goals:

1. Performance of compiled C++ code: This recompilation is necessary for exploiting optimization opportunities involved the altered private members. In fact, these days, compilers goes beyond even that situation: _linking_ code may trigger partial re-compilation without any changes to the source.

2. Zero-overhead abstractions: Having a private member should not in itself cost you in terms of performance.

However, we can make the class implementation opaque (e.g. using the PIMPL idiom: https://stackoverflow.com/tags/pimpl-idiom/info ) if we want/need to.

> Outstandingly complicated grammar

C++ is guilty as charged here! It even has a semi-idiom named the "most vexing parse": https://en.wikipedia.org/wiki/Most_vexing_parse

But there is an excuse, which is: Backwards compatibility. First, with C and C's syntax; then, with earlier versions of the language. Fixing the complicated syntax would break all existing C++ versions. So, they don't do it.

> No way to locate definitions

The fix for this is now part of the language: Modules. Unfortunately it will take time before these are widely-enough adopted.

> No run time encapsulation... C and C++ are not designed to run in managed environments

This is again necessitated by the performance requirements. Run-time encapsulation would mean slower code, sometimes much slower.

Also, such encapsulation would mean there would be room for another C++-like language, below C++, without this encapsulation... and a goal of C++ is not to leave such room.

Luckily, in recent years we have undefined behavior sanitizers in compilers, which catch a lot of these cases.

One should also remember that languages with managed runtime environments often have exceptions to the rule, i.e. unsafe/unmanaged sections (unsafe in Rust, JNI in Java).

> No binary implementation rules

Sorry to have to harp on this, but platform-specific ABI is another necessity of performance. Still, C++ has a long way to improve in this respect, e.g. https://stackoverflow.com/q/58339165/1593077

Recently, stack traces have been introduced into the language, and in C++23 they might be available by default when throwing exceptions; see: https://wg21.link/p2370r2

(I may continue in another post or extend this one.)


Pimpl seems to me like a workaround for deficiencies in the language. It's verbose and inconvenient. We should be able to add private member functions to a class without having to recompile all files that use the class. We should be able to forward-declare a class including public member functions without also declaring the member variables of the class.

Even in C I can say:

struct A;

void public_member_function(A *a);

I can also add as many static functions as I like later without affecting the public declararation of struct A.

Why not in C++?


You may do that in C++, too. It would look, literally, exactly the same. It would compile, and run, identically. And, it would be exactly as error-prone as the same code compiled with a C compiler.

Extra support in the language for automating PImpl types could be welcome, but might not offer enough benefit to offset the complexity.


Why do you feel that it's more error-prone?

function(a);

vs.

a->function();

I can accept that some think the second version looks nicer. But I don't buy that we should have to jump through hoops like pimpl just to get there for non virtual calls.


Nobody cares how it looks.

What matters is that your "a" is a naked pointer. Pointers have reference semantics, not value semantics, so are inherently error-prone. A pointer might be null. A pointer might be already freed. A pointer might be left over from some other use.

A PImpl object is a value, with value semantics. It owns the wrapped pointer, and whatever that points at. When it goes out of scope, it cleans up. If you throw, it cleans up. When it is copied, the right thing happens, if that is allowed, or cannot happen, if not. When it is passed to a function, the right thing happens. When it is returned from a function, the right thing happens. The "right thing", in each case, is visible in exactly one place in the source code, and nowhere else. Nothing using it can do any of it wrong.

This is all really fundamental stuff.


PIMPL is not a workaround for language-level deficiencies, it's a workaround/pattern for ensuring a forward-compatible ABI.


(continued from parent post)

----

> No reflection ... impossible to ... iterate over the methods or the attributes or the base classes of a class ... [or] programmatically determine the type of an object...

There's run-time reflection, and compile-time reflection. C++ has almost no runtime reflection - and this is again due to performance considerations. That would have a lot of overhead.

As for compile-time reflection, C++ has gained some over the past decade. decltype() gives you the type of an object. A static reflection Technical Specification is out, see: https://en.cppreference.com/w/cpp/experimental/reflect . It will allow much of what the author was decrying. It may make it into C++23, and some compilers already partially-support it. Now Is it very late in arriving? I suppose it's fair to say that. But TBH, it's quite difficult to design this for a 40-year-old language with a lot of baggage and not within a managed runtime. And achieve near-consensus about it.

----

> Very complicated type system

The type _system_ of C++ is not very complicated. What the author seems to dislike is how common standard-library types are actually somewhat complex constructs within the type system rather than atomic types or simple constructs. A language in which "string" is an atomic type might have std::map<string, string> rather than the complex-sounding `map<std::basic_string<char, std::char_traits<char>, std::allocator<char>>`, `std::basic_string<char, std::char_traits<char>, std::allocator<char>> >`. Still, that longer type is simply `std::map<std::string, std::string>`, and if you `use std::string;` then it's `std::map<string, string>`. (Yes, there are other template parameters to map, I know, I'm making a point).

In fact, one could argue that it is a virtue of C++ of having a non-complex type system: The complex types are actually implemented _in C++_, with no need for a rich gallery of atomic types. Other systems put things like lists, associative arrays, optionals (a.k.a. maybe's) etc. out of the reach of "mere mortals".

Then again, the choice of primitive types or type constructs in C++ is not great due to inheriting from C. C++ can construct new types as arrays-of, structs-of, unions-of, references-to, pointers-to and bit-fields-of. arrays are annoying with their decay behavior and unassignability, and non-discriminated unions are also useful mostly for low-level works. It would perhaps have been nicer if arrays behaved like std::arrays and unions like std::variants, and for the C-style types we would have used reinterpret-casts and other dirty tricks. But then, what would you want to happen when a constituent element of a variant throws an exception during construction? Is the variant in a valid state after that exception is caught? The answer is not at all trivial, so richer atomic types require making choices that aren't always right for everyone.

> if your function accepts const std::vector<const char*>& ... and I have a std::vector<char*> object ...then I can't pass it to your function

Well, not exactly. I mean, you actually can pass it to the function, if you insist: `my_func(reinterpret_cast<std::vector<const char*>&>(my_vec)` ... but that's not very nice. So, yeah, this is definitely an issue.

However:

1. There is no inherent reason that type T<E*> should be usable as a T<const E*>. Those could be completely different types For T = std::vector the types just happen to correspond nicely. I doubt that other languages address this, though.

2. If you want to be a wise guy, and tell me "other language would just have a string and a const string instead",, then - you wouldn't have this problem, since there is no such thing as an std::vector<const E>; that doesn't compile.

3. If your function takes `std::span<const some_string_class>`, you again don't have this problem.

so, a bunch of mitigating arguments + you can still do it if you really want to.

----

> Very complicated type-based binding rules

They're only complicated if you make them complicated. If you single f(), there won't be any complex overload resolution; and if you write a bunch of f()'s, there will.

As for the standard library, it's overload resolution can get complicated; but error messages have improved significantly! First, we got static_assert()'s in C++11 which explain what's the problem with (hopefully) simple text. Then in C++17 we got some deduction guides, so you fail less often. Then in C++20 we got concepts - which themselves are quite complex, but you don't have to worry about them; the point is that your error messages for overload resolution improve again. Don't get me wrong: There will still be long errors about failure to resolve, but they're more readable these days.

----

> Defective operator overloading

The only argument I saw here was:

> overloaded operators have to return their results by value - naively returning references to objects allocated with new would cause temporary objects to "leak" ...

1. In C++, we pass things by value unless there's a good reason not to.

2. If you're worried about copying when returning - that doesn't happen. This used to be a compiler optimization, but since C++17 the language guarantees it: https://en.cppreference.com/w/cpp/language/copy_elision . The return type doesn't even have to be copyable or movable!

3. Don't use new, it's rude these days: https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines... ; if for some reason you must allocate dynamic memory, use std::unique_ptr, or std::shared_ptr, or return an allocating container class instance, by value.

---

> Defective exceptions

C++ exceptions could of course be improved in various ways (e.g. they may soon be getting stack traces); but not along the author's line of argumentation.

> If we use exceptions, we have to write exception-safe code - code which frees all resources when the control is transferred from the point of failure

You need to write "exception-safe" code anyway! Acquire resources on construction, release them on destruction. Also known as CADR or RAII.

> ... and the vast majority of "resources" happens to be memory, which is managed manually in C++

Effectively - no, it isn't. And that's exactly because we're writing that "exception-safe" code: Since we _don't_ just write new nor delete (see above)

> To solve this, you are supposed to use RAII ... meaning that all pointers have to be "smart"

1. No they don't - only the _owning_ pointers need to be smart (or in container classes etc.); and the non-owning pointers you don't have to free.

2. What's your obsession with pointers, anyway? Pointers in C++ are so last-millenium...

----

> Duplicate facilities

Yeah, C++ has some of those. I mentioned std::array vs C arrays and std::variant vs C unions. That's the backwards compatibility for you... but also, don't mistake _similar_ facilities for _duplicate_ ones, like the author does:

> If you need an array in C++, you can use a C-like T arr[] or a C++ std::vector<T>

Those are different! `T arr[N]` has fixed size determined at compile time, and its elements typically resides on the stack; `std::vector<T>` has variable size and its elements typically reside on the heap (allocated with new). In fact, C++'s standard library is _missing_ many important array-like classes! See: https://stackoverflow.com/a/67409339/1593077

> That's because C++ doesn't have garbage collection, since that, folks, is inefficient.

It's not just inefficient; by now, it's effectively unnecessary. See: https://stackoverflow.com/a/48046118/1593077




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

Search: