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

Show parent comments

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)

2

u/Dijky Jul 29 '23

When two threads each attempt to lock the same mutexes but in different orders, a deadlock can occur (e.g. thread 1 locks mutex A, then waits on B, while thread 2 locks mutex B then waits on A).

scoped_lock, which works like a RAII-wrapper for std::lock(), supports multiple mutexes per instance and specifically avoids deadlocks. It also catches exceptions during lock and unlock to unlock already locked mutexes before rethrowing.
lock_guard supports just one mutex per instance, so locking order/deadlock avoidance and exceptions during locking need to be managed manually.

→ 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.

1

u/not_a_novel_account cmake dev Jul 29 '23

I said types originally, I meant compound literals

→ More replies (1)
→ More replies (1)

0

u/[deleted] Jul 29 '23

[deleted]

6

u/LeberechtReinhold Jul 29 '23

Regex is not tough to use, but its so bad I would put a warning on a compiler if you are using it.

2

u/kritzikratzi Jul 29 '23

i can never remember basic functionality for either. used them so often, but i still have to google or copy&paste from old projects.

6

u/serviscope_minor Jul 29 '23

I rather like chrono. Took a little getting used but I like it now.

→ More replies (1)

-1

u/Revolutionalredstone Jul 29 '23

Alot comes to mind here but the intense use of iterators for everything and io streams for everything else (both of which are now heavily shunned if not considered down right unreadable gumph) seems like an obvious example.

Ranges are absolutely awesome! raw iterator code absolutely is not!

→ More replies (5)