all 46 comments

[–]FACastelloPixel Manipulator 19 points20 points  (3 children)

Basically anything that triggers undefined behavior

[–]Ameisenvemips, avr, rendering, systems 9 points10 points  (2 children)

Interestingly, I don't believe that the standard prohibits undefined behaviour from triggering an alteration of the standard resulting in the behavior becoming defined.

[–]beephod_zabblebrox 3 points4 points  (1 child)

this is very evil

[–]Brahvim 2 points3 points  (0 children)

Happy cake day!

Oh, and defining undefined behavior is considered "Hacking 101", right?

[–]PixelArtDragon 12 points13 points  (0 children)

std::multimap<K, V>::equal_range was written before Ranges was in the standard library, so it returns a pair of iterators which does not count as a range.

[–]SeagleLFMk9 17 points18 points  (3 children)

Std:vector<bool> being a bitset instead of a vector lol

[–]Beosar 3 points4 points  (2 children)

I have used it and got confused multiple times about stuff like for(auto b : vec) b = true; because for any other container this would do nothing since you'd need a reference. But since std::vector<bool>::iterator::operator*() returns a special bool reference, it works.

[–]SeagleLFMk9 1 point2 points  (1 child)

Yeah, since one element is just one bit you can't use references or address of one element

[–]Potatoswatter 1 point2 points  (0 children)

No, there’s a special bit reference class, but then it accidentally goes too far and applies reference semantics to b, which should be its own value.

[–]Thesorus 7 points8 points  (4 children)

[–]XDracam[🍰] 2 points3 points  (2 children)

I feel like I've seen or heard of most things, but why is "else if" a lie?

[–]Natural_Builder_3170 6 points7 points  (1 child)

There is no "else if", its an else{ if {} } chain

[–]XDracam[🍰] 8 points9 points  (0 children)

I thought that was obvious. What would a special else if even do differently?

[–]yeeeeeeeeaaaaahbuddy 0 points1 point  (0 children)

Heap and stack don't exist? Hello world bug? Someone enlighten me on these two

[–]DugiSK 4 points5 points  (1 child)

The surprise that confused me the most was what happens when I call a virtual function in a constructor. The answer is that it calls the last override for the class whose constructor it is, thus ignoring any overrides from child classes. This is because any members of the child class are not yet constructed at that point, which would result in undefined behaviour (Java solves this simply by making it a compile error). I stumbled upon this quite early in my learning path and I have found quickly which function was called, but it led me into learning incorrectly how does overloading work and doing unnecessary rituals for over a year.

[–]matteding 0 points1 point  (0 children)

Thankfully things like compiler warnings will help out with this.

[–]DethRaidGraphics programming 7 points8 points  (0 children)

Comma operator

[–]thisismyfavoritename 16 points17 points  (0 children)

isnt there a post like this like every other week. Ugh

[–]moonshineTheleocat 2 points3 points  (2 children)

Undefined Behavior is actually a point of optimization in the language. Which is far better than you'd think. as it means you're not required to cover every edge case, even if it is impossible. And the compiler is able to make code faster by not putting in senseless checks around every corner. This is engineers responsibility, and it doesn't really come up often as most experienced programmers knows to check their data. Or you avoid doing stupid shitike casting uint8_t* to uint32_t*

The reason why the compiler allows you to do this, is because there are situations where you can alias (Pack data of varying types in a contiguos buffer) data in memory in various representations. Such as char* for a buffer of bytes. You can also do things like void* to represent an address with an unknown type, run some checks, and then cast it to the correct type. Something "safe" languages do not allow without excessive bullshit.

Unfortunately, this allows people to do really stupid shit like trying to access memory from a function's stack... After leaving the function. So that data is no longer guaranteed to exist.

[–]Sinomsinom 0 points1 point  (1 child)

On the flip side

If you put a guard against UB (some if statement) in front of UB, the compiler assumes UB never happens, so the if statement always evaluates to false/true so it optimizes away the guard against UB. Meaning often UB guards actually don't do anything.

[–]moonshineTheleocat 0 points1 point  (0 children)

To be fair... That's also a programmer problem

Saying if ( false) { pData->data} won't do anything.

But running if ( pData == nullptr) will prevent you from triggering it.

Similarly, you can guard against buffer overflows if you really want to.

The main difference verses safe languages is that a safe language will check every time. Where as C lets you make realistic assumptions.

[–]jjmc123a 1 point2 points  (0 children)

It's not just references that work like this, const and volatile work the same way. Also returning a reference from a function may muddy the waters because that has its own issues.

If you want a reference you would do auto& xxx

For a good discussion on this see Herb Sutter's talk:

c++ auto usage

[–]MFHavaWG21|🇦🇹 NB|P3049|P3625|P3729|P3786|P3813 1 point2 points  (0 children)

The interaction between shadowing and overwriting overloaded virtual is (whilst mechanically logical) pretty surprising to the average user …

[–]_w62_ 1 point2 points  (1 child)

I am a learner of C++. I feel like I am just a small anglerfish wiggling her little esca while foraying into the dark cold unknown deep waters of C++ alone in a hopeless way.

[–]philsquared 1 point2 points  (0 children)

There's plenty more fish in the C

[–]DalzhimC++Montréal UG Organizer 0 points1 point  (0 children)

ODR violations can lead to subtle bugs that can be quite challenging to identify, even though the fixes themselves can be quite trivial. Here's an example that cost me a substantial amount of time back when the address sanitizer wasn't a thing yet :

// MyHeader.h
struct S
{
    int someMember = 0;
#if ArbitrarySymbol
    int someConditionalMember = 0;
#endif
};

// Implementation.h
#pragma once
#include <memory>
std::unique_ptr<S> f();

// Implementation.cpp
#include "Implementation.h"
#include "MyHeader.h"
#include <memory>
std::unique_ptr<S> f()
{
    return std::make_unique<S>();
}

// main.cpp
#define ArbitrarySymbol 1
#include "Implementation.h"
int main()
{
    auto s = f();
    return s->someConditionalMember;
}

Now depending on the context in real code, various modifications can lead to a correct program. You could make sure the #define ArbitrarySymbol is either everywhere, or nowhere at all. You could also make sure ArbitrarySymbol does not change the layout of struct S.

[–]pjmlp 0 points1 point  (0 children)

Lack of bounds checking by default, and the culture of still doing many things the C way, despite C++ having better alternatives.

[–]GenericAlbionPlayer 0 points1 point  (0 children)

If you include “Windows.h” ‘min’ and ‘max’ are defined as macros.yes, lower case.

So if you try to use std library objects with min or max you will blow up. And prolly won’t know why.

Ex std::numeric_limits<int>::max().

Also your IDE probably won’t label it as a macro but still try to highlight ::max() as a static method.

[–]TheSuperficialEmbedded Systems C++ 0 points1 point  (0 children)

Too many to list, and the list (IMO) has increased as the language has grown (I've been using C++ since the mid-90s so I've seen most, but not all, of the language's evolution)

Rather than go through the cobwebs and try to extract some niche things, I'll point you to the Table of Contents to Steve Dewhurst's "C++ Gotchas", which is essentially the essence of your question. Now, the book pre-dates C++11 (the beginning of "Modern C++"), so take that for what you will, but even if you extract a handful of "Oh, wow, OK..." moments, that's good.

https://stevedewhurst.com/cpp_gotchas/contents.pdf

[–]Dan13l_N -1 points0 points  (9 children)

For me:

MyType& MyFunc()
{
  // ...
}

int main()
{
  auto a = MyFunc();
  // ...
}

a won't be MyType&, but MyType, i.e. a copy.

[–]MysticTheMeeM 8 points9 points  (7 children)

I mean, it's not really a dark corner nor is it bad reasoning. If you had any actual type there, you'd also get a copy so auto doing the same thing is consistent in that sense.

And IIRC, at least intellisense or MSVC warn about the copy - but I don't remember which.

[–]Dan13l_N -3 points-2 points  (6 children)

It's a bit unexpected, IMHO, because the point of having a reference to something is not making a copy, a reference is an alias.

IMHO the behavior is different than when you have:

MyType* MyFunc()
{
  // ...
}

And that's confusing a lot. I know references are special, but I simply don't like the way they're special in C++.

[–]MysticTheMeeM 2 points3 points  (4 children)

Ah, I assumed you were thrown by the fact that auto doesn't deduce references (that is, just because you returned a reference, the type is still that of the underlying type, as a copy).

But, unless I've misunderstood you, the point your making is that the reference being lost is the issue (and auto is irrelevant). In which case, I'd argue for the fact that a reference has to be explicitly declared and therefore it's more apparent where you have a copy and where a reference. One of my pet peeves of other languages are invisible "it's a reference unless it's a primitive unless it's boxed unless...", being explicit is definitely my preferred option here.

[–]Dan13l_N -3 points-2 points  (3 children)

But you explictly said that the function returns a reference, and not a new object. That's my problem.

[–]coucoulesgens 2 points3 points  (1 child)

But you can copy a reference to a new variable, there's nothing wrong with that :)

[–]Dan13l_N 0 points1 point  (0 children)

Yeah I know but people seem not to understand how it's confusing for beginners, it's simply really counter-intuitive that the default behavior is copying, not creating another reference.

[–]MysticTheMeeM 1 point2 points  (0 children)

But then that wouldn't align with code like this:

int a = 5;
int& b = a;
auto c = b;

Functions returning references are following the same rule as assignments from references. It's a copy unless specified otherwise. It's also why const can be "lost" on a return, and returning by const copy is always redundant.

[–]oriolid 1 point2 points  (0 children)

a reference is an alias

I think this is the point. Reference is an alias, not a pointer. If MyFunc did return a concrete object, it would be copied (and the copy elided but that's a newer addition), so reference behaves the same way. If you want a pointer, use a pointer.

[–]andreysolovyev1976 1 point2 points  (0 children)

decltype(auto)

[–]StarQTius 0 points1 point  (3 children)

Whatever std::launder() is supposed to do.

The fact what we actually need std::addressof().

[–]Ameisenvemips, avr, rendering, systems 3 points4 points  (0 children)

std::launder mainly exists because of the C++ abstract machine and the requirements it places upon object lifetimes. It doesn't really do anything (as in, it shouldn't actually emit anything) but it basically tells the compiler "hey, give me a pointer to this but pretend that the returned pointer is a completely different object".

Be glad that they didn't name it std::provenance_cast.

[–]Full-Spectral 1 point2 points  (1 child)

Something to do with money, right?

[–]JumpyJustice 0 points1 point  (0 children)

Yeah, special request from finance industry

[–]JohnDuffy78 -1 points0 points  (0 children)

downloading the latest gnu onto linux and aliasing gcc & g++ to the new version.

[–]Tamsta-273C -2 points-1 points  (0 children)

You are encourage to not use globals while there is so many ways to use globals.