4 ms·
How can the C++ statement below even compile? Since "1" and "2" are strings (not even chars). std::vector<int> v = {"1", "2"} // UB Rather than undefined b
by metadat 2y ago
How can the C++ statement below even compile? Since "1" and "2" are strings (not even chars).
std::vector<int> v = {"1", "2"} // UB
Rather than undefined behavior I'd expect a type violation error. But my C++ has gotten a bit rusty.
- ramon156 2y agoId argue that c++ is not type safe. Yes, strings could be converted to ints in theory, but these types are obviously not the same, why not make it more explicit?
- maleldil 2y agoType safety isn't binary but a spectrum. It's obvious that C++ is more type-safe than JS but less than Rust, which is less than Haskell.
- eru 2y agoWell, at some points it becomes even more complicated than just a spectrum. Your types in different languages just track different things. Eg Haskell's types (normally) don't track lifetimes nor ownership, but Rust does that. In contrast, Haskell likes to track whether side-effects like IO can occur at all, while Rust is happy to just let you eg open a file almost anywhere.
- chuckadams 2y agoHaskell has linear types now, so there's your lifetime and ownership, though thankfully they're optional... wrangling linear types makes Rust look friendly and lenient.
- eru 2y agoNice! I've only used linear types in Clean ages ago, but haven't checked them out in Haskell, yet.
- JonChesterfield 2y agoC++ is obviously hilariously less type safe than JavaScript. JavaScript will convert values from one type to a other at runtime but it's always absolutely sure what the type of the value is. C++ will compile code that looks reasonable, decide it's UB, not tell you about that and proceed to do abject nonsense at runtime. Considering C++ the more "type safe" one of the two is so far from accurate that I wonder if you've mistyped the name of one of the languages.
- eru 2y agoI have a lot of sympathy for your point of view. Though have a look at Rust: it's generally considered much more typesafe than both JavaScript and C++, but it also has (some) dark corners of undefined behaviour with approximately all the same baggage as C++. See also https://news.ycombinator.com/item?id=8206562 https://news.ycombinator.com/item?id=8206562 for a different point of view: dynamically typed languages are equivalent to statically typed languages with just a single static type.
- mofeien 2y agoI agree. C++ is statically typed, as in everything has a type known at compile time. But it is also weakly typed, as in types can be be converted into others implicitly which only makes sense a fraction of the time and readily introduces accidental UB at other times.
- jandrewrogers 2y agoYou can make it both stricter and more explicit. This is a 30 year old API from when C++ was a very different language. In a modern context, this example was explicitly designed to be as loosely checked as it is, it isn't intrinsic. Backward compatibility means they can't change this API to not allow these cases even if it is straightforward to do so.
- GrantMoyer 2y agoIt's simple: "string" literals in C++ are pointers to characters. std::vector has a constructor which takes a begin and end iterator and creates a vector with contents copied from that range. Pointers are iterators in C++, and chars are implicitly convertible to ints, so overload resolution selects the aforementioned constructor. The pointer to "1" and the pointer to "2" don't point into the same object, so trying to iterate from one to the other eventually causes a dereference past the end of "1" which is undefined behavior.
- metadat 2y agoI don't know about simple, haha, but thank you so much for explaining in depth!
- jiggawatts 2y agoThis... this is why I gave up C++ programming and have not looked back.
- mwkaufma 2y agoIt's matching std::vector's implicit constructor which takes two iterators, which can be initialized with any pointer. Note that it won't compile with one, three, or more string-literal char*s, but exactly two.
- metadat 2y agoWhy only exactly two? The whole "UB but it still compiles" thing, is pretty gross.
- comex 2y agoBecause the constructor takes two arguments, a "begin" iterator and an "end" iterator. And pointers count as valid iterators. Normally you wouldn't use the "= {}" syntax to invoke a constructor this way. Instead of `std::vector<int> v = {begin, end}` you would usually write `std::vector<int> v(begin, end)`. But for some reason C++11 decided to make those two things mostly equivalent.
- UncleMeat 2y agoThis sort of UB detection requires bespoke work. The compiler probably can figure out that the begin and end iterators here are referencing different objects but if you add just a bit more complexity to the code then the compiler won't be able to prove that.
- mwkaufma 2y ago
- ycombinatrix 2y ago>my C++ has gotten a bit rusty i see what you did there