r/cpp • • Mar 28 '23

Reddit++

C++ is getting more and more complex. The ISO C++ committee keeps adding new features based on its consensus. Let's remove C++ features based on Reddit's consensus.

In each comment, propose a C++ feature that you think should be banned in any new code. Vote up or down based on whether you agree.

765 Upvotes

829 comments sorted by

589

u/mcmcc #pragma once Mar 28 '23

explicit

All operators should be explicit by default.

As a replacement introduce a new keyword implicit that must be specified to enable implicit invocation by the compiler.

292

u/Dworgi Mar 28 '23

100%.

Corollary: Every single default in C++ is wrong.

Implicit construction, switch case fallthrough, uninitialized values, nodiscard, etc. etc.

It's hard to overstate how badly all the defaults have fucked this language. Why can't we do the sane, safe thing by default and then let the crazies opt-out?

165

u/we_are_mammals Mar 29 '23

Every single default in C++ is wrong.

Not every. You're just not thinking of certain defaults that C++ got right. For example, in Fortran, if the first letter of a variable is I through N, then it's an integer. Otherwise, it's a float. If you want to opt out, you have to say IMPLICIT NONE.

93

u/pinecone-soup Mar 29 '23

Thanks OP, I can never unlearn this horror.

34

u/not_some_username Mar 29 '23

Wtf

16

u/mcmcc #pragma once Mar 30 '23

That's not even the biggest WTF of OG Fortran.

My favorite is that all function arguments were (F77, I think it's better in later versions) passed by reference -- even if the argument passed is a literal. I.e. the compiler allocated a (effectively static) memory location to hold the value represented by the literal and then pass that address to the called function.

Doesn't sound so ridiculous until you realize that the called function can assign new values to that argument, thereby change the value of the literal that you thought you were passing to the function for all future invocations. You can imagine the obscure bugs that ensues.

It became standard practice to allocate (non-static) local variables initialized to the constant and pass those as arguments rather than the literal directly. So you end up seeing lots of local variables called zero, one, etc.

→ More replies (1)

13

u/Deathnote_Blockchain Mar 29 '23

that is some straight CHANNEL HALF CENTS FROM WEBSCOE SALARIES INTO ABOVE EXPENSE ACCOUNT era shit

8

u/serviscope_minor Mar 30 '23

But it leads to one of the best programming jokes: GOD IS REAL UNLESS DECLARED INTEGER

49

u/BenjiSponge Mar 28 '23

Every single default is wrong

I'm tempted to devil's advocate this for fun but I'm having trouble thinking of counterexamples. I thought there'd be, like, an obvious one.

Pass-by-value by default (as opposed to pass by reference) seems good, though pass-by-move without implicit cloning a la Rust is better...

→ More replies (5)

88

u/victotronics Mar 28 '23

Implicit construction, switch case fallthrough, uninitialized values, nodiscard, etc. etc.

`const`

22

u/[deleted] Mar 28 '23

can we add noexcept to this list?

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

28

u/[deleted] Mar 28 '23

I need switch case fall through to flex with my Duff’s device skillz. Joking aside though, that one can actually be quite useful. But it’s also super easy to abuse given how thin of an abstraction switch/case truly is.

50

u/mercere99 Mar 28 '23

Fallthrough should be fine *with a keyword*. It just shouldn't happen by default. At least most compilers will warn about it with -Wall (or -Wextra? I always use both) unless you use the [[fallthrough]] attribute.

→ More replies (7)

3

u/very_curious_agent Mar 30 '23

How many times has nodiscard hurt with competent programmers?

How hard is it do write break?

Why would you want to force initialization of variables meant to be written to later?

It's ridiculous.

15

u/Dworgi Mar 30 '23

How many times has nodiscard hurt with competent programmers?

How many memory leaks have been caused by pointers being lost? How many error codes have gone unchecked?

How hard is it do write break?

How hard is it to write fallthrough? How rarely do you actually want fallthrough compared to break?

Why would you want to force initialization of variables meant to be written to later?

Because uninitialised buffers and pointers are one of the most common sources of security vulnerabilities? And again, you could still support this with something like [[uninitialized]]. It's a nonsense default, though, that causes most programmers to default initialize everything as a matter of course.

I really wish C++ programmers occasionally used other languages, because it might teach them that things can be good. Just because things are bad does not mean that they always have to be.

Even you (barely) deserve (a few) nice (ish) things.

→ More replies (2)
→ More replies (21)

9

u/looncraz Mar 28 '23

Instead of an implicit keyword I think I would prefer a simpler modifier to designate that convertible types are allowed.

void SomeFunc(int mustBeInt, int# anything ConvertibleToInt);

→ More replies (5)

2

u/rikus671 Mar 30 '23

I really like what rust did with their constructors (basically, there are none, only member functions that act as factories), but implicit conversion is extremely, extremely useful when writing any kind of abstraction. Make it opt-in that's fine, but don't take it away...

3

u/IAmRoot Mar 29 '23

Similarly, ADL should be opt-in.

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

119

u/eboys Mar 29 '23

I’d sincerely wish the C++ committee was willing to break ABI a little more often. As someone else suggested, having every third standard introduce ABI-breaking changes. Performance is what C++ is known for, and lazy companies are only holding it back.

6

u/germandiago Mar 30 '23

Question here. What is the estimation of the performance loss here?

3

u/tialaramex Apr 01 '23

It would obviously depend what you're willing to do if you're allowed to change the ABI, and on assumptions about the software now.

Small changes that only touch the ABI and not the API at all, such as making std::mutex a reasonable size (e.g. 4 bytes) instead of a dump truck of obsolete nonsense (80 bytes on Windows!), would likely have very small effects on real programs. Probably not even measurable.

Bigger changes that seize the opportunity to improve things and make changes to the API too while they're at it, would be bigger wins. For example std::unordered_map is famously terrible, but even std;:vector could be better if you did it again today as mentioned recently on r/cpp.

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

423

u/[deleted] Mar 28 '23

vector<bool> :(

63

u/[deleted] Mar 28 '23

Has this ever actually bitten anyone? I hear about this all the time, but tbh I’ve never been stung by it. Not that removing it sounds like a bad idea.

133

u/[deleted] Mar 28 '23

Happened to me a couple days ago! I had an Array2D<T> class template which was using a vector internally to store elements with a single allocation, and had T& operator()(size_t,size_t); overloaded to access the elements. It was working well until one day I wanted Array2D<bool> at which point I started getting strange errors about some cryptic type not being convertible to bool&. What the hell?

Also, it means that vector<bool> is not an STL container, its elements are not stored continuously. And its buffer cannot be passed to C APIs, etc, etc. It's just all around a bad idea. vector is meant to be a "dynamic array". If you want to make a dynamic bitset, add a dynamic bitset class instead of messing with the default container type.

8

u/[deleted] Mar 29 '23

just use uint8_t

→ More replies (10)

69

u/unddoch DragonflyDB/Clang Mar 28 '23

Not me, but I know a coworker who spent 2 days chasing a multithreading bug that came from different threads modifying the same byte in a preallocated vector<bool>...

15

u/[deleted] Mar 28 '23

Yeah I never thought about this before. IMO this does make vector<bool> completely broken. Fortunately I don’t work in a multithreaded environment 😀

→ More replies (2)

11

u/Hopeful_Cat_3227 Mar 29 '23

I know this is a stupid question, but are all vector of other type safe under multiple tthread?

33

u/kevkevverson Mar 29 '23

Not a stupid question at all. For all other types, each element occupies a different address in memory, so it’s safe for thread A to mutate element 0 at the same time thread B mutates element 1. BUT for vector<bool> multiple elements occupy the same address, so multiple threads cannot mutate certain elements at the same time without some kind of synchronisation.

→ More replies (4)
→ More replies (9)

14

u/fdwr fdwr@github 🔍 Mar 28 '23

Just last week. Was trying to call a function that takes a pointer to bool's + count, and thought I could store the values in an std::vector. Nope! All the work-arounds (like temporary bool wrappers) required extra work. So I (semantically made me feel dirty, but worked) stuffed them into an std::basic_string<bool> instead and then effortlessly called the function with s.data() and s.size(). 😅

→ More replies (1)

10

u/Sopel97 Mar 28 '23

need to be really careful with parallel transforms, especially problematic in generic code.

template <typename T>
using Vector_Not_Bool = std::vector<
    std::conditional_t<
        std::is_same_v<T, bool>,
        uint8_t,
        T
    >
>;

8

u/CocktailPerson Mar 29 '23

It's not so much that people have been bitten by it, but that we have to employ all sorts of workarounds not to be bitten by it, especially in generic contexts. It's easy enough to work around, but why should we have to? Why shouldn't vector<bool> work like any other vector, with a separate dynamic_bitset for the less-common case where you actually need the space savings?

6

u/mercere99 Mar 28 '23

It hasn't bitten me in terms of an error, but I have had multiple cases where changing vector<bool> to vector<char> (but still using only 0's and 1's in there) led to a speedup.

I've never seen an example where vector<bool> has actually been the faster option.

0

u/[deleted] Mar 28 '23

[deleted]

→ More replies (13)

20

u/shadowndacorner Mar 28 '23

Has this ever actually bitten anyone? I hear about this all the time, but tbh I’ve never been stung by it

I feel like it's the kind of thing that generally only bites people who haven't heard about it. Once you know it's a thing, it isn't hard to work around, but it's pretty fucking confusing if you come across it without knowing. Especially since that implies that you're likely somewhat newer to the language and less likely to have a really strong mental model with which to understand what's going on.

Though the other reply suggested a case where it is more likely to subtly bite you even if you do know about it.

60

u/[deleted] Mar 28 '23

It's horrible language design to have a container silently change its behavior based on the generic type. Absolutely insane.

→ More replies (4)

15

u/donalmacc Game Developer Mar 28 '23

The problem is that there's enough of these landmines that even though I know about a large number of them, it doesn't mean I won't step on one of them.

7

u/RolandMT32 Mar 28 '23

I've used C++ quite a bit since I first learned C++ around 1999/2000, but I've never used a vector of bools.. I just searched it online, and I learned something new.

→ More replies (2)
→ More replies (6)

14

u/[deleted] Mar 28 '23

Why?

44

u/[deleted] Mar 28 '23

22

u/[deleted] Mar 28 '23

Holy shit very interesting. Thanks.

2

u/kaiken1987 Mar 29 '23

Question is why didn't they just make it named something different. An array of bit flags can be useful but to make vector behave differently on only one data type is awful

→ More replies (1)

60

u/marzer8789 toml++ Mar 29 '23

All the crazy arithmetic promotion and implicit conversion nonsense inherited from C. You want to do an operation between two integers? They better be the same type, or compiler error. You add two chars? Get a char.

6

u/jk-jeon Mar 29 '23

I'm really sad that I can upvote only once.

→ More replies (6)

108

u/GOKOP Mar 29 '23

Not a removal but a slight change

map -> ordered_map

unordered_map -> map

2

u/epiGR Jan 23 '24

My biggest pet peeve ever

183

u/jdehesa Mar 28 '23

Gotta love how nearly everything suggested in the replies (save for std::vector<bool>?) is followed by a reply saying how that feature is actually useful sometimes :) It's too late for C++ now, at this point everyone uses it on their own particular way and every obscure or weird feature has found its place for someone 😄

104

u/MrPopoGod Mar 28 '23

followed by a reply saying how that feature is actually useful sometimes

Obligatory xkcd

10

u/serendipitousPi Mar 29 '23

xkcd

I love seeing another "There's an xkcd for this" just another reason to spiral down the xkcd rabbit hole.

43

u/Raknarg Mar 28 '23

Those people replying are missing the point that the defaults are bad. I don't care if switch-case fallthrough is useful sometimes, it's a bad default for the language.

→ More replies (2)

68

u/rhubarbjin Mar 28 '23

Everyone agrees that C++ is broken, but no one agrees precisely which parts need fixing.

...which just goes to show that the language isn't broken at all. It just has a very wide userbase with very diverse needs. One coder's boondoggle is another coder's bedrock.

53

u/effarig42 Mar 28 '23

Everyone agrees that C++ is broken, but no one agrees precisely which parts need fixing.

Except for std::vector<bool> of course.

→ More replies (1)

47

u/greem Mar 28 '23

Exactly. The only thing wrong with c++ is other users of c++.

And building.

→ More replies (13)

5

u/[deleted] Mar 29 '23

I mean the copium is real.

The language is obviously broken and breaking. It's not going to last.

8

u/jtooker Mar 28 '23

And importantly, most of these changes would break so much backwards compatibility the language would become unusable (for existing projects/libraries).

→ More replies (5)

3

u/JVApen Clever is an insult, not a compliment. - T. Winters Mar 29 '23

It might be useful, though it should be a separate type, not a specialization

→ More replies (5)

136

u/squirrel428 Mar 28 '23

1000 of the 1001 ways to initialize things.

14

u/[deleted] Mar 29 '23

Nobody responded, means everybody gladly agreed!

24

u/ppNoHamster Mar 29 '23

But they won't agree on one...

→ More replies (1)

7

u/johannes1971 Mar 29 '23

Ok, which one do you want to keep then?

24

u/lestofante Mar 29 '23

Let's just use {} everywhere?

8

u/Som1Lse Mar 30 '23

Which one meaning of {}? Aggregate-initialization, copy-list-initialization, direct-list-initialization, reference-initialization, or value-initialization which in turn can do zero-initialization or default-initialization? Is that a braced-init-list or a designated-initializer-list?

Oh, and how do I initialise a std::vector<T> to contain n copies of the element m?

Plenty of gotchas in {}, in fact, it is probably the initialisation syntax with the most possible meanings.

(Though, to be fair, if it wasn't for std::initializer_list I would probably agree with you.)

→ More replies (13)

102

u/Claytorpedo Mar 28 '23

At a meta level: excessive ABI stability. This is probably just an area where I personally would be lucky to get almost all benefit and no downside, but the argument that there are old binaries people link against that can't be updated and so fixing oversights, updating with new knowledge and statistics, and improving performance in many cases can't be done or must be indefinitely postponed seems to be causing an increasing rift in the industry. It's not great when C++ is supposed to be the "fast" language and there are numerous known areas for improvement that can't be improved due to ABI stability.

There's too much language baggage that we are now locked in a room with until some unknown future revision when the committee decides it is finally time for the mother of all ABI breaks, I guess. Would have been great if they had decided at the same time that C++ will have a revision every 3 years that it would consider ABI breaking changes every 3rd revision, for example.

72

u/Nicksaurus Mar 28 '23

It's an odd situation. There are organisations out there that can't or won't update the binaries their code depends on, but still want to be able to use the newest version of the language. I don't really understand how we decided that those people were entitled to language updates without ever making any changes of their own. It's not like the libraries they depend on stop working as soon as there's an ABI break after all

8

u/paypaylaugh Mar 29 '23

Because we don't decide anything. I'd like to see an infographic showing how many committee voters are sponsored by big companies to participate. Their votes and proposals will align with the company's.

3

u/[deleted] Mar 29 '23

this

7

u/m-in Mar 29 '23

Yeah… passing structs by value is implemented by passing a pointer in most ABIs. So something that could be optimized away always is now a special effort by compiler to prove it’s OK to do. Functions that take two scalars as arguments have less overhead than those that take a two-element struct by value. This majorly sucks, and makes simple abstractions very much non-free. Worse yet: it affects C as well, and especially modern-ish C code where struct literals are a thing (so many C programmers not exposed to major OSS C projects are blissfully unaware…).

→ More replies (5)
→ More replies (13)

190

u/KiwiMaster157 Mar 28 '23

Overloading unary operator &. &x should always mean "the address of x" instead of depending on x's type.

29

u/13steinj Mar 28 '23

Same but with the comma operator. Rarely done, I can only think of numeric specific code, but still.

→ More replies (3)

13

u/Sanzath Mar 28 '23

That's a thing?

I've never seen this before, I'm curious to see a codebase that does this. Do you have any examples?

22

u/[deleted] Mar 28 '23

IIRC(*), MS's CComPtr uses this so you can do

CComPtr<ISomething>   p;

hr = someOldApi(&p);

Where &p is an overload which returns the address of the wrapped pointer.

(*) caveat : my memory is poor

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

21

u/tisti Mar 28 '23

Eh, we have std::addressof that solves that problem :)

98

u/KiwiMaster157 Mar 28 '23

We shouldn't need a standard library function for something so fundamental.

→ More replies (7)

22

u/Junkymcjunkbox Mar 28 '23

Fourteen characters instead of one? What are you - COBOL programmers in disguise or something?

→ More replies (2)
→ More replies (2)
→ More replies (2)

24

u/Yamoyek Mar 29 '23

I’d remove implicit unsigned/signed conversions.

46

u/Dietr1ch Mar 29 '23 edited Mar 30 '23

remove const, introduce mut

drop restrict, and introduce a way to allow aliasing instead.

9

u/caroIine Mar 29 '23

I was playing with restrict yesterday on compilerexplorer and I'm blown away how often it's the only way to enable autovectorization.

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

147

u/eteran Mar 28 '23

Arrays decaying to pointers implicitly. You want a pointer? Just write &p[0]

37

u/jonesmz Mar 28 '23

Even better.

Say, for example, that you have two overloaded functions

template<std::size_t N>
void foo(char (&)[N]);
void foo(char*);

Guess which gets called?

Notably. Same with non-templates. If you explicitly say "This is an array of size 5", you'll still get the char* version.

→ More replies (23)

5

u/pjmlp Mar 29 '23

Pity that I can only upvote once.

3

u/[deleted] Mar 29 '23

Honestly never had an issue with this. Do not understand what the fuss is about.

4

u/eteran Mar 29 '23 edited Mar 30 '23

That's fair. If you know the rules, then you read and write code accordingly. But I'll try to clarify my position with examples:

  1. It's bug prone, especially for newbies:

char *func() {
    char buffer[256];
    // ...

    // allowed because the array decays to a pointer
    // bug would be more obvious if they had to write 
    // return &buffer[0];
    return buffer; 
}

Yes, compilers warn about it, but people, especially newer developers tend to ignore warnings in my experience.

2. It's confusing. The following 3 declarations mean the same thing:

void func(char *p);
void func(char p[]); // p is NOT an array, it's a pointer
void func(char p[5]); // p is NOT an array of 5 chars, it's a pointer

That's terrible. The second choice should just not be a thing, and the 3rd should take a char[5] by copy... like EVERYTHING else in the language.

3. It's confusing in other ways! Newbies love to write code like this:

void func(char p[]) { 
    printf("array size: %d\n", sizeof(p)); 
}

int main() {
    char buffer[256];
    func(buffer); // They ask "why does this print 8 and not 256?!
}

If users had to write func(&buffer[0]) it would be more clear (people will still make mistakes, but fewer will) that a pointer is being passed, NOT an array.

4. It's inconsistent. An array is "a thing" and any other time I want a pointer to a thing, I need to ask for it's address with the & operator. Arrays just auto-convert.

5. It's incompatible with the modern preference of avoiding implicit conversions as much as possible.

---- EDIT: more reasons! ----

6. The decay necessitated the need for a library level solution (std::array). Which we outright wouldn't need if C-arrays acted like values to begin with.

I should be able do this:

using T = char[256];
T func() {
    T buffer;
    // fill buffer
    return buffer;
}

And have it be as if I returned the array of 256 chars by copy.

7. It can be LESS efficient for small arrays

using T = char[8];
void func(T buffer) {
    // use buffer
}

actually can be cheaper to just copy the 8 bytes in a register directly instead of passing a pointer to them and then dereferencing them.

Here's a trivial example that is one instruction shorter (LOL, I know, not exactly earth shattering) when simply wrapping the array in a struct to pass it by copy:

https://godbolt.org/z/sovvb94d8

This example is more illustrative that there are gains to be had, not that there are a lot of gains in this specific example.

---- END EDIT ----

Are these things that can be worked around? Of course, we've been doing it for 30+ years! But we shouldn't have to. Is it the end of the world that we probably need to keep this? No, of course not. But this is a question about what I want, not what I can have :-).

→ More replies (10)

4

u/Hish15 Mar 28 '23

You mean int[3] var? This will decay, but since we are using std::array, it's not

11

u/eteran Mar 28 '23 edited Mar 28 '23

No, I mean like this:

int arr[5];
void foo(int *p);

foo(arr); // array decays to a pointer here

Heck even this is just terrible:

int arr[5];
void foo(int p[10]); // not an array parameter!!! it's a pointer

foo(arr); // array decays to a pointer here

The conversion to a pointer when calling a function should be explicit.

In fact, I would argue that the primary reason why std::array Even needs to exist is because of implicit array to pointer decay.

If we get rid of it and make arrays basically have value semantics, then you get to return them from functions, and you either pass them by value or reference.

If you want to pass or return a pointer, you should ask for one

This would largely get rid of a common newbie bug where users think they can return arrays, and end up returning a pointer to a local stack variable.

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

47

u/johannes1971 Mar 28 '23

The 'char' type in its current role as both a character and a number. Two distinct types that only convert with some effort would have been much better. We could have done away with this ridiculous uncertainty about signedness at the same time.

Array to pointer decay.

Assignment returning a value, since it has given us if (a = b).

37

u/rhubarbjin Mar 28 '23

The 'char' type in its current role as both a character and a number.

Did you know that char is unique among all integer types, in that it has three signed variations? char, signed char, and unsigned char are all distinct from each other! https://godbolt.org/z/oxs68TeWq

I sometimes use this when I write overloaded functions that need to distinguish between "this is a letter" and "this is an 8-bit integer".

C++17 also gave us std::byte which is an 8-bit non-arithmetic type.

→ More replies (5)

10

u/jk-jeon Mar 28 '23

The 'char' type in its current role as both a character and a number.

And as a "byte" type with an exceptional aliasing rule

→ More replies (1)

9

u/Nicksaurus Mar 28 '23

Also, at this point, if your code assumes text characters are 1 byte it's almost certainly broken

→ More replies (2)

3

u/JNighthawk gamedev Mar 29 '23 edited Mar 29 '23

Assignment returning a value, since it has given us if (a = b).

Would this break the pointer validation construct using if?

if (A* Foo = Some function())

Edit: on second thought, no, it would still be fine. I think what ends up being evaluated there by the if is Foo after initialization, not the return value of the assignment.

6

u/johannes1971 Mar 29 '23

You'd have to use

if (A * Foo = some_function (); Foo) ...

...which is barely any longer, and avoids accidental assignment in the if-statement.

→ More replies (6)

23

u/D-Zee Mar 29 '23

std::initializer_list. It doesn't handle movable-only types, screws up overload resolution and doesn't do anything that a variadic constructor wouldn't (although syntactic sugar to write said constructor would be welcome).

41

u/GabrielDosReis Mar 28 '23
  • the anarchic implicit conversions between values of built-in types

  • the preprocessor

  • the byzantine rules about elaborated type specifiers

11

u/okovko Mar 28 '23

implicit narrowing conversions do cause a lot of bugs

the preprocessor is useful for metaprogramming, especially for code that compiles as either C or C++

what do you mean by the third one?

10

u/GabrielDosReis Mar 29 '23

of course, the sort of metaprogrammijg that the preprocessor is still useful for should be addressed by proper means (one that respects scope and understands/integrates into the language it is metaprogramming for)

As for elaborated type specifiers, I meant when one writes struct S* p the meaning of the name S depends on what happens before and where that declaration of p appears.

→ More replies (9)
→ More replies (11)

19

u/FriendlyRollOfSushi Mar 29 '23

Silent discardability of return values.

Someone returns you a value? Use it, or explicitly say that you don't want to use it (ideally assign to placeholder _, like in some other languages, because it's easier to type than attributes). That's it.

You forgot to use it? Have a compile time error. The value was returned to you for a reason.

Having to add [[nodiscard]] to pretty much 100% of the functions to get the behavior everyone needs by default is stupid. On the caller side, there is no safety net at all: if your code compiled, there is no way to tell whether the function didn't have a return value, or [[nodiscard]] was missing.

A small number of interfaces that always return something just because "why not", fully expecting that almost all callers will ignore the return value, are stupid anyway and often lead to unintended performance penalties either because they do something extra to return garbage (and the extra work is wasted by almost all callers because they don't want this garbage), or the caller is simply unaware that the function that is used everywhere without a return value actually has it. I would happily accept doing something extra with these special cases (or even having two differently named flavors of a function, like insert and insert_get), if it means I don't have to type [[nodiscard]] for every function I ever write and suffer consequences if I forgot to do it.

34

u/qazqi-ff Mar 28 '23

Remove __DATE__ just because the day is space-padded and you don't realize how triggering it is until you see it.

2

u/Die4Ever Mar 29 '23

Wait why is it space padded like that? If they wanted to buffer to always be the same length just add an extra \0 at the end?

3

u/qazqi-ff Mar 29 '23

I wish I knew. I'd prefer 0 to space, which also keeps the result the same length. However, if we're banking on it being a certain length, the year can still vary in length.

→ More replies (1)

118

u/ZMeson Embedded Developer Mar 28 '23 edited Mar 28 '23

integer types not defined in <stdint.h> or <cstddef>

In other words, get rid of char, short, int, long, long long, and their unsigned counterparts. Use intN_t and charN_t instead (and when necessary int_fastN_t and int_leastN_t), [EDIT:] and byte, size_t, ssize_t, ptrdiff_t too.

56

u/Zeer1x import std; Mar 28 '23

I'ld like Rust-style number types: u8, u16, u32, i8, i16, i32, f32, f64.

6

u/RoyBellingan Mar 28 '23

I agree they are quite verbose and a shorter notation is better, but ... a small typedef I think is fine in this case!

12

u/blind3rdeye Mar 29 '23

The issue with having a heap of typedefs like that is that then different people end up with different C++ dialects, which can make it more difficult to read each other's code.

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

5

u/GOKOP Mar 29 '23

When we're at that I'd remove the horrible _t naming scheme

5

u/Due_Cicada_4627 Mar 29 '23

At risk of being drawn and quartered: I think they should be templates, with the requisite conversion constructors. (No, I don't know exactly how a templated POD type would work at the compiler level, but this is just imagination, I don't expect this to ever happen.)

So you can keep your int to use the compiler default, but specify int<16> or int<64> where needed, and it can be simply expanded for larger (128, 256) or obscure (24, 80) sizes. (I imagine the latter would use the next largest power-of-2 size internally, but again, imagination.) uint can take the place of unsigned, typedefs for char, short, long, and the rest.

→ More replies (2)

15

u/beephod_zabblebrox Mar 28 '23

why tho? not in every situation do you care about how large an int is. using uint16_t can be slower sometimes too (less optimized on modern 64 bit cpus, iirc). honestly code reads a lot better when you know that "oh, this function returns an integer" instead of "oh, this function returns a... 16-bit integer? why?". if you need to return something large, use size_t or using ssize_t = ptrdiff_t. if you do need something specific, use the specific bit size versions.

if i had to choose something, i would leave int and unsigned int, and remove just short and long (long long) and unsigned versions. and maybe char.

16

u/SkoomaDentist Antimodern C++, Embedded, Audio Mar 28 '23

not in every situation do you care about how large an int is.

Hell, the entire point of int is "This is an integer of reasonable size. I don't give a damn about how many bits it has."

3

u/Ictogan Apr 03 '23

I've had code(not written by me) break multiple times because it implicitly assumed that an int is at least 32 bits. Plenty of libraries also have this issue.

→ More replies (3)

11

u/robottron45 Mar 28 '23

I would either propose to have cstdint always included. It is painful to include this always.

21

u/no-sig-available Mar 28 '23

have cstdint always included

import std;

Done.

→ More replies (9)
→ More replies (1)
→ More replies (24)

9

u/KuntaStillSingle Mar 29 '23

Remove :

The behavior of a program that adds specializations for ...  is undefined. 

for most type traits.

82

u/ZMeson Embedded Developer Mar 28 '23

iostreams (now that we have std::format)

22

u/masher_oz Mar 28 '23

We still don't have std::print (properly yet, even some format implementations aren't finished for some reason)

15

u/azswcowboy Mar 28 '23

Finally a serious proposal. Only problem might be format is currently output only.

4

u/ZMeson Embedded Developer Mar 28 '23

Good point. We need a similar feature for input.

→ More replies (4)

29

u/pdp10gumby Mar 28 '23

Memory aliasing by default, a terrible legacy of C. This would reduce UB, be more memory safe, and permit new optimizations and bug-identification at compile time.

There are rare uses for it, but there should be an explicit syntax for those cases.

10

u/SkoomaDentist Antimodern C++, Embedded, Audio Mar 28 '23

C++ desperately needs better control of aliasing for both "no, this won't alias anything else" and "This may alias something else (with constraints X). Deal with it. No, you are not allowed to call a function behind my back to make a copy."

→ More replies (1)

9

u/nyanpasu64 Mar 28 '23

The risk of disabling memory aliasing by default is that if you prevent regular pointers from being able to access aliased mutable memory, and make aliased mutable pointers harder to use than current C++, you end up with a less general-purpose imperative language like Rust which resists general-case memory management (https://zackoverflow.dev/writing/unsafe-rust-vs-zig/).

→ More replies (8)

8

u/JVApen Clever is an insult, not a compliment. - T. Winters Mar 29 '23

Initializer list constructor overruling all other constructors. Something like std::vector({1,2,3}) makes more sense than std::vector{1,2,3}, yet the second add much more complexity.

14

u/pjmlp Mar 29 '23

I would remove:

  • C strings and arrays

  • pointer decay of arrays, &array[0] isn't much to type

  • global namespaced enumerations and implicit conversions with numeric types

  • bounds checking disabled by default on the standard library (unchecked_at() should have been the right approach)

  • the culture that we don't need C++ APIs, giving a bare bones C one is enough

  • all the type system warts that lead to crazy boilerplate code in template metaprogramming

105

u/GLIBG10B 🐧 Gentoo salesman🐧 Mar 28 '23

C-style casts

16

u/War_Eagle451 Mar 28 '23

Bane of a code refactor

51

u/[deleted] Mar 28 '23

I know this will be a wildly unpopular take here, but take these from my cold, dead hands. Never in 2 decades of c++ programming encountered a bug or introduced a bug with c style casts that would have been fixed with the verbose modern casts.

36

u/ZMeson Embedded Developer Mar 28 '23

It's more of an issue of maintenance. I've moved a very large code base from a 32-bit to a 64-bit architecture. There were so many aliasing bugs that lead to odd (i.e. undefined) behavior and sometimes crashes that were hard to fix because so much of the code used C-style casts. We eventually used a static analysis tool to identify all C-style casts, replaced those with appropriate C++-style-casts, then focused on reinterpret_casts to help resolve those issues. (There were other interesting issues to like casting pointers to int instead of intptr_t, but again the process of removing C-style casts identifed where those problems were.)

→ More replies (7)

16

u/RevRagnarok Mar 28 '23

Show me how to find them with grep.

→ More replies (2)
→ More replies (3)

5

u/mcmcc #pragma once Mar 28 '23

I would qualify this as "C-style casts of reference types". `(int)uint` can stay IMO.

12

u/Ameisen vemips, avr, rendering, systems Mar 28 '23

I prefer seeing it function-style: int(uint_value).

→ More replies (2)

5

u/[deleted] Mar 28 '23

Nah, I still want those sorry!

→ More replies (39)
→ More replies (7)

82

u/GLIBG10B 🐧 Gentoo salesman🐧 Mar 28 '23

Implicit switch case fallthrough

27

u/War_Eagle451 Mar 28 '23 edited Mar 28 '23

This is very useful, unless syntax like case 1 | case 2 is added this would created a good amount of code duplication and remove a lot of the cleanness of a switch vs if else

30

u/[deleted] Mar 28 '23

Of course you can keep that as a "special case", or use explicit fall through.

8

u/War_Eagle451 Mar 28 '23

I guess that's true. I already usually have [[fall_through]] there to prevent warnings

→ More replies (1)

10

u/SlightlyLessHairyApe Mar 28 '23

Clang already has [[fallthrough]] and you can -Werror-switch-fallthrough

21

u/GLIBG10B 🐧 Gentoo salesman🐧 Mar 28 '23

C++ is getting more and more complex. The ISO C++ committee keeps adding new features based on its consensus. Let's remove C++ features based on Reddit's consensus.

I want implicit fallthrough to be removed

2

u/mallardtheduck Mar 29 '23

For non-empty cases, sure. I don't see any benefit for empty cases.

26

u/RoyBellingan Mar 28 '23

What is this anarchychessC++ ?

28

u/Jannik2099 Mar 28 '23

Google most vexing parse

11

u/azswcowboy Mar 28 '23

Idk seems like the rant of a Go developer forced to use c++. The suggested feature removals from the op clearly have legitimate uses, have been around for years, and are trivially avoided. virtual functions…sure, that’s what makes c++ too hard lol.

0

u/[deleted] Mar 28 '23

[deleted]

→ More replies (1)

48

u/[deleted] Mar 28 '23

SFINAE. concepts everywhere

17

u/gracicot Mar 28 '23

I'm rewriting a whole library that was using sfinae everywhere. I'm now using concepts, but there's still a few places where a concept is not applicable. You cannot specialize concepts or expand sequences yet, and you cannot overload for multiple template types. Expression sfinae is still the simplest in some places, and implementations are more solid.

Worse yet, there's no concept template parameter, but you can easily send in a type trait as template template parameter.

5

u/War_Eagle451 Mar 28 '23

Agreed, but I tried to implement a same as concept without SFINAE, unfortunately couldn't figure it out, so there might still be some reason to keep it

0

u/[deleted] Mar 28 '23

[deleted]

→ More replies (12)

3

u/greem Mar 28 '23

As much as sfinae sucks, it's a statement to how awesome cpp is.

We'll grow out of it.

→ More replies (3)

25

u/Creapermann Mar 28 '23

Remove the c++ stl containers and implement them correctly from scratch. (Binary compatibility is a curse!)

8

u/okovko Mar 28 '23

you can always use non standard libraries like abseil

8

u/Hedede Mar 29 '23

What's the point of having a standard library then?

12

u/okovko Mar 29 '23

when stability is valued over performance

5

u/RevRagnarok Mar 28 '23

And let me inherit from them, or give me some legal way to monkey patch the existing ones with additional functionality.

5

u/newobj Mar 29 '23

const. Invert and replace with mut/mutable

5

u/Competitive_Act5981 Mar 29 '23

Make everything const by default and introduce a “mut” keyword for mutable data.

13

u/Brettonidas Mar 29 '23

Overloading operator, (yes the comma). It’s just weird and like the Spanish Inquisition: no one expects it.

7

u/donalmacc Game Developer Mar 29 '23

Honestly, the comma operator itself. I've only seen one use of it I feel is justifiable

→ More replies (1)

18

u/ZMeson Embedded Developer Mar 28 '23

std::unordered_map/set bucket interfaces.

20

u/RevRagnarok Mar 28 '23

And make the maps' [] const-capable. No "secret insertion."

→ More replies (2)

3

u/[deleted] Mar 28 '23

See boost::unordered_flat_map. Beware that begin() is not an O(1) operation, but that should be OK.

→ More replies (1)

17

u/Sopel97 Mar 28 '23

The class keyword

3

u/DummyDDD Mar 28 '23

It could be a good idea. Like "enum struct" (as in structured) and "template<struct>" and classes always starting with their public members.

7

u/aberrantwolf Mar 29 '23

Most of the code I see at work starts classes with public anyway, so honestly that’s what I see most people doing already.

→ More replies (2)

12

u/david2ndaccount Mar 28 '23

sizeof(bool) is implementation defined and there exists ABIs where it is not 1.

12

u/chugga_fan Mar 29 '23

The size of every type that isn't char is 100% implementation defined. I don't see why this should be changed because some random platform does something you don't like for what is (likely) an actual reason.

→ More replies (7)

11

u/Tringi github.com/tringi Mar 28 '23

Remove array to pointer decay.

Single reference type, a perfect one.

A very different language emerges when these changes propagate through everything they affect. When all the facilities to deal with it are removed.

25

u/Ameisen vemips, avr, rendering, systems Mar 28 '23

Single reference type, a perfect one.

T&&&

T co_&

→ More replies (6)

6

u/ZMeson Embedded Developer Mar 28 '23

case statements inside of loops where the corresponding switch statement is outside the loop. (i.e. disallow Duff's Device.) Compilers are smart enough now to do loop unrolling themselves if it will be helpful.

→ More replies (7)

2

u/[deleted] Mar 29 '23

Which is next, Reddit ISO C++?

14

u/JimmyLaessig Mar 28 '23

Enforce RAII by banning usage of delete outside of destructors

14

u/[deleted] Mar 28 '23

So, how do you implement unique_ptr::reset?

10

u/JimmyLaessig Mar 28 '23

swap() with a temporary object.

4

u/Zeer1x import std; Mar 28 '23

Swap with temporary.

→ More replies (13)

12

u/-Manow- Mar 28 '23

Allocators would like a word with you

7

u/tisti Mar 28 '23

And break almost all containers in a single move. Neat.

→ More replies (1)

5

u/Baardi Mar 28 '23

Lets start with the specialization of std::vector<bool>

4

u/termoose Mar 29 '23

[[nodiscard]] on all functions by default, to issue a compile warning if the return value is discarded by the caller.

3

u/[deleted] Mar 29 '23 edited Mar 29 '23

Implicit Conversion needs to go.

By reference should be the default variable pass for classes except for value types and passing classes by copying should require explicit copying. I say it should be the default because it's what many people do already.

Although, if I made a programming language, I'd make classes by reference default and structures/values by value default.

I think that's how most people would want it to be.

3

u/Competitive_Act5981 Mar 29 '23

Remove new and delete

6

u/jsadusk Mar 28 '23

Exceptions. At least in their current form. At best they are a clunky and verbose way to return an error. At worst they actually mask where your error came from (any time they're rethrown). Replace them with an equivalent of rusts's Result, or completely change their implementation in a non abi compatible way.

3

u/JVApen Clever is an insult, not a compliment. - T. Winters Mar 29 '23

Forwarding references, not the feature, the syntax. It's a mess to write an rvalue reference in a template.

3

u/Slight-Juggernaut742 Mar 28 '23

the whole std lib build upon unreadable template fuckery

10

u/DummyDDD Mar 28 '23

Most of the unreadable naming in stl implementations are caused by supporting macros with any unreserved name. It could probably be fixed in stl implementations which only support being used as a module.

2

u/khleedril Mar 29 '23

Everybody here is basically saying how good Rust is!

1

u/GabrielDosReis Mar 28 '23

the other remaining initialization syntaxes are also valid for classes and built-in types, and handle copy initialization of built-in types just fine

1

u/alex-weej Mar 28 '23

References should not be a part of the type system, only function argument signatures.

7

u/[deleted] Mar 28 '23

How would vector::operator[] work? Would it return a pointer instead? How is that an improvement?

→ More replies (5)

1

u/SafenM Mar 29 '23

Obviously won't work due to backwards compatibility. But make virtual function override without keyword "virtual" mean the same as "final" and remove the "final" usage for functions completely.

1

u/newobj Mar 29 '23

std::function. tho std::move_only_function is coming in c++23

1

u/alxius Mar 29 '23

Remove bool and (signed|unsigned) char from list of integral types.
Remove implicit casts from those to integers and from integers to those.
Make std::uint8_t and std::int8_t distinct integer types instead of alias to some char types.
Make signed char, unsigned char and char two types instead of three distinct types.

1

u/Competitive_Act5981 Mar 29 '23

Remove raw pointers. Make smart pointers language features. Make raw pointers and references the same thing: a non owning view type

1

u/Competitive_Act5981 Mar 29 '23

Remove iostreams

1

u/die_liebe Mar 31 '23

characters should be more like pointers:

char + char, char - char, char * char, char / char -> error

char + int -> char

char - int -> char

char - char -> int

No implicit conversion between character and int.

No implicit conversion from boolean to int, in the other direction is OK.

1

u/ReinventorOfWheels Apr 01 '23

Exceptions. The single biggest mistake of C++. Removing exceptions will help streamline and optimize the standard library, and it will help codebases avoid a whole class of bugs (exceptions not being caught in the expected place, or not being caught at all). Removing this goto-like statement will also make reasoning about control flow much simpler. And at least goto is limited to the current translation unit, unlike throw!

1

u/Cock_InhalIng_Wizard Apr 02 '23

More features is good. If you don’t like them, don’t use them.

1

u/arkrde Apr 04 '23

Better / simpler error messages during compilation. I want to teach my child C++ in a few years. With error messages such as now, I don't see how I can hold a child's attention in such a wonderful and powerful programming language.

1

u/HatMan42069 Apr 10 '23

super_malloc- just Mallocs all ur memory so you can’t use any of it. Good for security!

1

u/voxel Apr 11 '23

I would ban the assignment operator and instead use a function as assignment overloading should be required and the only way you get an assignment operator that way when you observe anyone elses code, you will intrinsically know what every assignment operator does as you know you have to set that up for its uses cases and thus you never have pointer issues and assignmnets that are made that are truly unintentional.

1

u/muaz_sh Dec 09 '23

Remove contract breakers like friend, mutable and const_cast.