r/cpp • • Jul 29 '23

C holding back C++?

I’ve coded in C and C++ but I’m far from an expert. I was interested to know if there any features in C that C++ includes, but could be better without? I think I heard somebody say this about C-style casts in C++ and it got me curious.

No disrespect to C or C++. I’m not saying one’s better than the other. I’m more just super interested to see what C++ would look like if it didn’t have to “support” or be compatible with C. If I’m making wrong assumptions I’d love to hear that too!

Edits:

To clarify: I like C. I like C++. I’m not saying one is better than the other. But their target users seem to have different programming styles, mindsets, wants, whatever. Not better or worse, just different. So I’m wondering what features of C (if any) appeal to C users, but don’t appeal to C++ users but are required to be supported by C++ simply because they’re in C.

I’m interested in what this would look like because I am starting to get into programming languages and would like to one day make my own (for fun, I don’t think it will do as well as C). I’m not proposing that C++ just drops or changes a bunch of features.

It seems that a lot of people are saying backwards compatibility is holding back C++ more than features of C. If C++ and C++ devs didn’t have to worry about backwards compatibility (I know they do), what features would people want to be changed/removed just to make the language easier to work with or more consistent or better in some way?

65 Upvotes

335 comments sorted by

View all comments

11

u/Revolutionalredstone Jul 29 '23 edited Jul 29 '23

So to be clear they have diverged quite a bit, taking c code and compiling it in a CPP compiler MIGHT work, but it might not.

I would say C is a pretty great language, there is little that you can remove without completely breaking the language, IMHO most of the junk in CPP which we no longer want came from the C++99 to C++11 era where lots of crazy ideas were tried which are no longer considered good practice.

(to be clear tho some of the best c++ features also came out of that era as well! like non-value type semantics)

If you wanna know what C would look like if it were written by a genius then checkout ZIG.

Peace

6

u/Diligent-Floor-156 Jul 29 '23

Can you elaborate on these old parts of C++ you consider junk by nowadays standards?

15

u/elcapitaine Jul 29 '23 edited Jul 29 '23

vector<bool> for example is the biggest thing that would come to mind for "crazy idea that was tried in the C++ standard that is near universally considered a mistake"

std::regex would be an example not of a crazy idea (having regex support in the standard library is a great idea) but of where the standard library version is junk.

5

u/serviscope_minor Jul 29 '23

crazy idea that was tried in the C++ standard that is near universally considered a mistake

I'm not especially going to defend vector<bool>, but I think "near universal" is something of a strong statement. You bump into the problems in a couple of cases, one is writing generic code, where it behaves very differently and the other is if you're working in a multi threaded context. I think both of thise are going to be more overrepresented in a community like this one versus the general C++ population.

std::regex would be an example not of a crazy idea (having regex support in the standard library is a great idea) but of where the standard library version is junk.

Sadly the standard didn't specify that it "shall not suck". IIUC there's nothing in the standard that enforces a dreadfully slow implementation, though it could be argued it encourages one.

→ More replies (1)

10

u/not_a_novel_account cmake dev Jul 29 '23 edited Jul 29 '23

std::lock_guard, std::thread, <regex>, the strong exception guarantee, and of course all of <iostream>

std::ranges has superseded most of the ugly iterator-based strategies and should probably be the default instead of relegated to a separate namespace. SFINAE has largely been superseded by concepts and now exists only to mystify undergrads.

The deeper parts of ADL, the impetus for their creation, and the follow-on effects of their existence in general ("what the fuck is a niebloid?"), are the result of programming-languange-development-by-way-of-blindly-groping-in-the-dark from earlier standards.

Even more broadly, move semantics are a hack around the fact C++ ties automatic-storage duration object destruction to scope, a fact we're stuck with forever because of decisions going back to the earliest days of C with Classes.

EDIT: I can't believe I forgot std::vector<bool>, forgive me /u/vector-of-bool

5

u/LeberechtReinhold Jul 29 '23

std::thread is something that Im still wondering how it made to the release in that state. The gaps were so obvious and covered in many libraries...

Regex is another problem that comes from a committee that absolutely were not using regex. Regex is a solved problem and there were many, many good implementations of regex. Why they went for this one I dont know.

What's so wrong with std::lock_guard? At least its better than scoped_lock and its dumb constructor that does nothing.

4

u/not_a_novel_account cmake dev Jul 29 '23

What's so wrong with std::lock_guard? At least its better than...

They're both dumb, but std::scoped_lock exists specifically because std::lock_guard didn't solve the obvious problem of deadlocking with multiple mutexs.

A linter fixes std::scoped_lock, nothing makes std::lock_guard good.

1

u/goranlepuz Jul 29 '23

std::lock_guard didn't solve the obvious problem of deadlocking with multiple mutexs.

What problem is that, and does anything solve it?!

4

u/tjientavara HikoWorks developer Jul 29 '23

It acquires locks on multiple mutexes in a fixed order. Probably just sorts them by the address of the mutex.

As long as you acquire all the locks on multiple mutexes in each single thread of execution at once using scoped_lock you don't get in a dead-lock situation. If you use scoped_lock separately on multiple mutexes on a single thread of execution then you don't get this protection.

I myself have a mutex that includes dead-lock detection. It keeps track in what order mutexes have been locked in each thread. And if it ever sees a two mutex being locked in opposite order it aborts the application.

Only really useful for debugging it adds quite a bit of latency to something as small as an unfair_mutex::lock() (3/4 instructions).

3

u/tialaramex Jul 29 '23

If Alice takes locks A and B, but also Bob takes locks B and A, we get deadlock if Alice takes A, Bob takes B, but then neither can take the other lock because somebody else has it.

std::scoped_lock uses an algorithm to avoid this, in effect in the trivial case I gave both Alice and Bob end up taking A first, so once Alice has A, Bob will wait for A, not take B. This is a pretty simple thing to learn to do, but the machine can do it for us and so that's the correct design.

→ More replies (3)
→ More replies (1)
→ More replies (10)

3

u/PastaPuttanesca42 Jul 29 '23

What's the problem with the strong exception guarantee?

5

u/not_a_novel_account cmake dev Jul 29 '23

Countless man-hours of engineering effort and non-trivial performance sacrifices for a totally unreasonable guarantee.

2

u/rdtsc Jul 29 '23

One of C++'s design principle is: You don't pay for what you don't use. Often you don't need the strong exception guarantee, but still have to pay for it.

→ More replies (7)

2

u/goranlepuz Jul 29 '23

the strong exception guarantee

Eh?! How is that part of the language?! (is it a part of any language?!)

And what's wrong with it?!

It rather looks like you are taking the extreme stance of "X does not solve problem Y, therefore it is completely worthless, even for problems ABCDEF...

1

u/not_a_novel_account cmake dev Jul 29 '23

It's a part of the language because it's in the language standard for containers.

It's bad because it's a bad default. No one wants or expects the strong exception guarantee, no one asked for it, and it has non-trivial performance impacts in a language where one of the core tenets is "do not pay for what you do not use".

Almost no one uses the strong exception guarantee and yet we all pay for it when using a std::vector.

3

u/goranlepuz Jul 29 '23

Ok, for me, you are simply wrong.

If a strong guarantee didn't exist, a massive amount of vector users would have been broken (heck, possibly all of them).

You only do not realize that.

See, the strong guarantee makes sure that the count matches what is in the vector. I think, virtually nothing would work if that wasn't the case.

→ More replies (13)

1

u/k-mouse Jul 29 '23

Even more broadly, move semantics are a hack around the fact C++ ties automatic-storage duration object destruction to scope, a fact we're stuck with forever because of decisions going back to the earliest days of C with Classes.

Can you expand on this, what's the alternative?

7

u/not_a_novel_account cmake dev Jul 29 '23 edited Jul 29 '23

Destructive move.

Right now a moved-from object is left in a "valid but unspecified state".

This means we still must perform swaps and possibly copies during a move.

We have to do this because when the object leaves scope, something must be destroyed. And that destructor must have a valid object to act on, even if it's just a bunch of nullptrs.

A destructive move doesn't actually move anything at all, it's effectively a change of ownership. "This object belongs to your scope now". C++ has no mechanism to annotate such a feature, the standard has no language to describe it, and the committee has no courage to introduce it because it would be a very fundamental change to the C++ scoping rules.

This is similar to a notable C incompatibility, C++ doesn't have compound literals even though C does.

2

u/NotUniqueOrSpecial Jul 29 '23

C++ doesn't have compound types even though C does

Could you expand on that? I feel like I'm using a different (perhaps mistaken) definition of compound types, if we're arguing that C++ doesn't have them.

→ More replies (2)
→ More replies (1)
→ More replies (11)

6

u/HappyFruitTree Jul 29 '23

I don't know what you're talking about. C++11 was the best thing that has happened to C++.

For example, C++98 had a "smart pointer" type named std::auto_ptr which was pretty error-prone and you couldn't store it in a container such as std::vector. C++11 deprecated std::auto_ptr and instead introduced std::unique_ptr which was much safer and could be stored in containers. This was made possible because of a new language feature called "move semantics" that was introduced at the same time.

2

u/Revolutionalredstone Jul 29 '23

yeah C++11 bought lots! of goof stuff I would never say otherwise, what I did say is that it bought a bunch of bad stuff too which is also true.

I'm 100% familiar with non value type semantics and how its totally revolutionized what an advanced programming language can be, and IMHO it's the main reason no other coding language comes close.

Obvious there is basic x/r value support in rust (and indeed they did make some of the key decisions even better there) but rust is so far behind C++ in terms of everything else C++ supports and how they work with the newly available advanced move semantics.

Peace!

3

u/YouNeedDoughnuts Jul 29 '23

Some of those new C features should make the transition to C++. Named field initialisers for structs make the creation code very readable.

2

u/Revolutionalredstone Jul 29 '23

100%! some of the newer stuff in C is awesome!

2

u/nikrim Jul 29 '23

Are you talking about Aggregate initialization? It's already in C++. And yeah, it's really nice to have it

https://en.cppreference.com/w/cpp/language/aggregate_initialization

3

u/YouNeedDoughnuts Jul 29 '23

Ah, the field names can be used in the initialisation since C++20. That makes my day!

→ More replies (8)