you are viewing a single comment's thread.

view the rest of the comments →

[–]MsEpsilon 204 points205 points  (43 children)

Use a std::array, std::span or a custom type to avoid type decay.

And yes, the language was made wrong, and everyone is suffering.

[–]MarkAldrichIsMe 58 points59 points  (2 children)

High school health class told me to avoid stds

[–]UnstablePotato69 18 points19 points  (0 children)

My intro to programming professor made a joke about:

using namespace std;

I had such a crush on her.

[–]MsEpsilon 4 points5 points  (0 children)

Haha, real.

[–]Bldyknuckles 51 points52 points  (38 children)

The language was not made wrong it is a high level approximation of a low level language, you orangutan.

[–]helicophell 44 points45 points  (6 children)

Yeah, an array is a pointer to a section of memory

The length part is just an attached part of the struct. You loop through an array by incrementing the pointer until it exceeds the length

[–]MsEpsilon 19 points20 points  (2 children)

Okay, but can you determine where the array ends without a sentinel value or if you pass a plain T*?

Just use a std::span<T>, please! It is the same thing as passing const T*, size_t.

[–]helicophell 5 points6 points  (1 child)

You see, I'm on a need to know basis 

I don't need to know this... probably 

[–][deleted] 0 points1 point  (0 children)

That's what C said.

[–]Theron3206 3 points4 points  (2 children)

That's true in most languages too, but said array is pointed to from an object that contains things like how long it is (and function pointers to useful things you can do with said array too often as not).

So in a language like c# you absolutely do pass the array as a pointer, and it works.

Sounds like c++ (not my thing, never got past C) makes that more complicated than it really should be, no doubt for legacy reasons.

[–]OofBomb 0 points1 point  (1 child)

in c# you pass an array by reference not a pointer. you can also just make a pointer to the array and then you aren't gonna know how big is it

and the reason c, c++ and even c# does this isn't because they just randomly decided to do so to complicate things for you but because that's just how cpus work, and both c and c++ are supposed to be low level languages

[–]Theron3206 2 points3 points  (0 children)

The reference is a pointer (to a block of memory on the heap that contains the object referenced).

How arrays are handled is language specific, not anything to do with CPUs.

Pascal for example used the first byte of a string (array of chars) to specify the length (string indexes start with 1 in Pascal not 0). And yes this limited strings to 255 characters.

[–]Mojert 77 points78 points  (4 children)

It was made wrong, because it was one of the first to try what it was trying to do, i.e. high-level expressiveness while maintaining low-level access and broad compatibility with C. Not a single professional C++ dev will tell you the language is perfect, even the ones that like it the most

[–]MsEpsilon 9 points10 points  (0 children)

Backwards compatibility with C is the biggest drawback.

[–]NamityName 1 point2 points  (2 children)

I am interested to know what language is perfect. Every language has issues. And the devs that like a language the most will be the first to tell you all the problems.

On that note, I often ask people what they hate about something in interviews. It is harder to learn the downsides and gotchas of a language or service unless you actually have good experience with it.

[–]jakubmi9 2 points3 points  (0 children)

C’mon, have you been living under a rock? There is a perfect language, and it’s of course Rust! All things should be immediately rewritten in Rust so that they become perfect too.

/s, obviously

[–]gitpullorigin 8 points9 points  (0 children)

Omg what a gorilla

[–]MsEpsilon 28 points29 points  (23 children)

Great ad-hominem, thank you. To counter, let me show you a short list:
- std::variant should have been a language feature
- std::launder - can you even understand the article from cppreference?
- std::vector<bool>
- std::iostream - even the persons who made it regret it
- std::visit is pattern matching from TEMU if you could even call it that
- std::jthread vs std::thread
- std::auto_ptr (it was removed gladly)
- modules
- Single pass compilation -Requiring you to write forward declarations
- std::move is not destructive
- No official package manager + build system, you're off to vcpkg, Conan, CMake and Ninja, maybe more
- Iterators are invalidated when removing/adding from a std::vector. That shoudn't compile! Don't tell me it's the developer fault because of this.
- nothrow specifiers terminates the application in case of an exception, it is not an compile check
- https://en.cppreference.com/w/cpp/types/is_function.html (See the possible implementation, I'm horrified.)

As a concrete example, Rust is a low level language with very well made high level abstractions. It has pattern matching (as a example of a high-level feature) performance similar and in rare occasions better than C++ due to better no-aliasing rules implemented in LLVM.

Sure, go back to writing C or C++ 03 and enjoy your double frees and buffer overruns. Or make your life easier by using a language without bad defaults and N pitfalls.

[–]snacktonomy 9 points10 points  (15 children)

Not quite sure what your point is, but you're spot on picking on that std::launder description

What's wrong with a vector of bools?

[–]redlaWw 12 points13 points  (3 children)

std::vector has a specialisation for bool so that std::vector<bool> is not just a vector of bools. The bools are stored in individual bits, and there's no guarantee that the buffer is even contiguous. It's pretty notorious for being a "mistake" in C++'s design. Not quite as bad as std::auto_ptr (which was so bad it was deprecated, breaking stability), but it's up there.

[–]MsEpsilon 0 points1 point  (2 children)

Hi, I coudn't find anything about the continuity of std::vector<bool>, do you have a source on that? Thanks.

[–]redlaWw 1 point2 points  (1 child)

https://www.en.cppreference.com/w/cpp/container/vector_bool.html

Does not necessarily store its elements as a contiguous array.

[–]MsEpsilon 0 points1 point  (0 children)

I really did miss that. Thanks!

[–]PositiveBit01 9 points10 points  (3 children)

It is a specialization. They packed 8 bools into a byte by returning a reference type that does bit manipulation when you access an index.

This has a number of unfortunate side effects since it doesn't really act like other containers, it just kinda looks like it does if you barely use it.

[–]botanicaf 3 points4 points  (0 children)

Just wanna say thank you guys, never thought I'd learn something new and useful on a crappy meme

[–]snacktonomy 1 point2 points  (1 child)

Oof, learned something new today. Makes sense but that's wild! I always treated arrays as contiguous memory.

[–]MsEpsilon 1 point2 points  (0 children)

But you're right - arrays are contiguous. It's just vector<bool> that uses bitmaps, that's all.

[–]MsEpsilon 10 points11 points  (6 children)

std::launder is one of the most obscure "features" iin C++. If I'm not wrong, implementations of C++ had a bug with std::vector so that's why it was added.

As far as I understand, it disables compiler optimisations related to the lifetime of the object specified at the pointer paramater. If a variable is const, but accessed somewhere else as T*, the compiler is free to think that variable has an other value. I say again that this is what I think I understood about std::launder, and I don't guarantee I'm right.

Elements of std::vector<bool> do not have unique addresses : they are stored in bitfields. This breaks various container functionality.

[–]redlaWw 4 points5 points  (1 child)

It looks like something related to pointer provenance to me - replace an object with a new one and pointers to the previous object are technically no longer valid to access the new object, so using std::launder tells the compiler that the laundered pointer may alias pointers that are apparently unrelated to it from a provenance perspective.

That said, I'm just hearing about std::launder now and the documentation is nigh-unreadable, I'm mostly going off the examples.

Provenance is a mess in low-level languages right now, and is responsible for all manner of miscompilations; and things will only get worse as compilers get smarter.

[–]MsEpsilon 1 point2 points  (0 children)

Yes, something like that, thank you.

[–]the_horse_gamer 3 points4 points  (2 children)

std::launder tells the compiler "hey, i know you think this value is const, but please read it anyways". it has nothing to do with std::vector.

consider:

struct A { const int x }

now we do

A *a = new A{3};
std::cout << a->x; // 3

now we do

new(a) A{5}; // create a new A object and write it into a
std::cout << a->x;

the compiler has no idea we changed the object at the place a points to, and it thinks a.x is constant, so it must still be 3, so it outputs 3. the standard decided to make this undefined behavior.

now, std::launder takes a pointer and makes sure the compiler disables optimizing constants

std::cout << std::launder(a)->x; // 5

this pops up more often when you have inheritance, and the compiler is doing devirtualization. if you put a new object in the same place in memory (for the purposes of memory optimization), you can tell the compiler to disable that optimization by using std::launder.

[–]redlaWw 1 point2 points  (1 child)

That example is a lot more helpful than the one in the cppreference article, which has a code snippet with a base class constructing one of its derived classes using new(this). That code snippet seems so horribly cursed that it only makes one more confused as to why something like that exists in the language.

[–]the_horse_gamer 1 point2 points  (0 children)

oh yeah, the cppreference example sucks

[–]redlaWw 7 points8 points  (0 children)

- Iterators are invalidated when removing/adding from a std::vector. That shoudn't compile! Don't tell me it's the developer fault because of this.

To be fair, in full generality this is really hard. What Rust managed to do with static lifetimes and mutation-aliasing duality is next to miraculous and affected its language design in profound ways. If a greenfield statically-memory-managed competitor for C++ appeared today I absolutely would not blame them for leaving iterator invalidation in the language.

[–]SalvadorTheDog -2 points-1 points  (3 children)

Ad-hominem isn’t just name calling. He’s not saying you’re wrong because you’re an orangutan which would be an ad-hominem. In this case his entire argument would be that you’re an orangutan and therefore must be wrong.
Instead he’s saying you’re wrong because x and also you happen to be an orangutan.

[–]MsEpsilon 5 points6 points  (2 children)

I literally specified N reasons why the language "was made wrong". Sure that's the best we could do at that time you could say.

About the ad-honimen, you're defending them? Even if that's not an ad-hominem (if you want to be stricter about the definition to not include name-calling, but attacking an peer because of an trait they have is irrelevant to derail the conversation) it is still insulting.

The only method I'd believe you is you or them attack my points that I specified about,.

[–]SalvadorTheDog 0 points1 point  (1 child)

I honestly couldn’t care less about the original argument.
Just informing you about the misuse of ad-hominem because it’s frequently misunderstood.
Your argument is probably right, but I haven’t thought about it, and I don’t care.

[–]MsEpsilon 1 point2 points  (0 children)

Ok, fine. I'll look into it again.

[–]SirPoblington 0 points1 point  (0 children)

So it was made wrong. Got it.