How the STL Uses Explicit
quuxplusone.github.io
quuxplusone.github.io
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.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.
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.
See also 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.
Backward compatibility means they can't change this API to not allow these cases even if it is straightforward to do so.
The whole "UB but it still compiles" thing, is pretty gross.
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.
Why I gave up C++.
That and because somehow package management is still a nightmare.
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.
i see what you did there
Except for the captured variables in a lambda which are const unless you use the mutable keyword. Not a bad idea though.
Is there a way to force capture by const-reference by the way?
int main() {
int x = 0;
[&x] { x= 1;}(); // works
[&x=std::as_const(x)] { x= 1;}(); // error: assignment of read-only reference 'x'
}
Not very pretty, but it works.Like having C structs magically turn into C++ ones, thus implicit rules like these.
Anyone that cares about C++ evolution should read "Design and Evolution of C++", not only for how it came to be, also for safety approaches over plain C, that Bjarne is stil arguing for to this day on WG21 meetings.
For example, the following code does not compile with either -std=c89 or -std=c++98, but does compile if we uncomment the constructor line:
struct Foo {
int x;
/*public: Foo(int x) : x(x) {}*/
};
int main() {
struct Foo foo = 5;
return 0;
}
$ gcc -std=c89 tmp.c
tmp.c: In function ‘main’:
tmp.c:8:20: error: invalid initializer
8 | struct Foo foo = 5;
| ^
$ g++ -std=c++98 tmp.c
tmp.c: In function ‘int main()’:
tmp.c:8:20: error: conversion from ‘int’ to non-scalar
type ‘Foo’ requested
8 | struct Foo foo = 5;
Maybe I'm missing something?Additionally, you are missing the whole package of struct semantics in C++, that while they should at naked eye still look like C structs, they have to also support C++ struct semantics, of memory construction, copy assignment and bitwise comparisaion.
Hence why structs and classes are the same, with the difference that structs are public by default, with code generated for keeping the bitwise C semantics, until any of those operations are redifined, at which point the compiler leaves out the job to the developer.
It seems like an unforced error in the language design, rather than a concession to backwards-compatibility.