std::vector<std::string> strings;
strings.push_back("Hello");
std::observer_ptr<std::string> p(&strings[0]);
strings.clear();
std::cout << p; // use after free std::vector<std::string> strings;
strings.push_back("Hello");
std::observer_ptr<std::string> p(&strings[0]);
strings.clear();
std::cout << p; // use after freeTo use the header I linked it should be `nonstd::observer_ptr<std::string> p(&strings[0]);`
You also need to dereference p to use the stream operator.
I'm disappointed you presented such a contrived example. You also deliberately avoid const-correctness. That's a little insidious. You need to const everything manually in C++, so as a good C++ programmer you should const everything as you write C++. Only carefully omit const when you need mutability.
We all know C++98 is dangerously unsafe. Let's write your example in C++11.
#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <utility>
#include "observer_ptr.h"
// kill the noise
using std::cout;
using std::vector;
using std::string;
using nonstd::observer_ptr;
int main() {
vector<string const> const strings { "Hello" };
observer_ptr<string const> const p(&strings[0]);
strings.clear(); // not gonna happen
cout << *p;
}
» clang++ -std=c++11 immutable.cpp
immutable.cpp:11:5: error: member function 'clear' not viable: 'this' argument has type 'const std::vector<const std::string>' (aka 'const
vector<const basic_string<char, char_traits<char>, allocator<char> > >'), but function is not marked const
strings.clear(); // not gonna happen
^~~~~~~
/usr/local/Cellar/llvm/HEAD/bin/../include/c++/v1/vector:735: 10: note: 'clear' declared here
void clear() _NOEXCEPT
^
1 error generated.
EDIT: Down voted for legitimate rebuttal. Nice.So now using const is some kind of difficult task? Not much cognitive overhead there. I thought everyone was onboard with immutability these days.
My point is that safe low-overhead data structures with non-dangling, pointers protected from nullptr dereferencing ARE possible in C++ and not Rust. Yes, you have to put const after every type. Call that vigilance if you want. It's a really small price to pay IMO.
Edit: Down voted again. What's wrong with my point or code? I'm curious.
The fact that if you make everything totally immutable you can write safe C++ is (a) not true; (b) even if it were true, a totally irrelevant point in practice since not everything can be immutable. "C++ is memory-safe if you don't mutate anything anywhere" is a completely uninteresting point to the real world (even if it were true).
> My point is that safe low-overhead data structures with non-dangling, pointers protected from nullptr dereferencing ARE possible in C++ and not Rust. Yes, you have to put const after every type. Call that vigilance if you want. It's a really small price to pay IMO.
No, it's completely wrong. You have no protection from dangling references in C++.
I'd love to see an example without globals or an undefined behavior factory function.
Also, consider this:
std::observer_ptr<std::string const> const foo() {
std::vector<std::string const> const strings { "Hello" };
return std::observer_ptr(&strings[0]);
}
std::cout << foo(); // use after free, even with const!
This is how these discussions always go: someone presents an example of use-after-free in C++, someone else says "that's contrived"/"not real C++", and the discussion continues indefinitely. At some point we are going to just start debating "does anyone actually write use-after-frees in modern C++?", which is also a question we have empirical answers to by way of vulnerability databases and bug trackers, and the results don't look good for C++ there either.If your contention is that observer_ptr is completely safe, then we could have easily added it to Rust. We didn't for a reason: it's unsafe in Rust and unsafe in C++.
They are protected from undefined behavior, performant, and are excellent middle ground between reference counting or raw pointers.
I like Rust I just don't understand how I'm wrong about this single little pro C++ has over it.
Are you talking about how you can write data structure abstractions in C++ using unsafe feature (like std::observer_ptr) that are themselves safe to use? If so, that's exactly Rust's model! Rust's linked lists, for example, are written using unsafe code internally and present a safe interface.
> I like Rust I just don't understand how I'm wrong about this single little pro C++ has over it.
Because it's not a pro that you can write unsafe code in C++ without saying "unsafe"!
It's fine, I get you're very invested in this. I'm gonna go back to making stuff. I like the work you guys are doing and I don't want to give the impression I do not.
I understand I sound irritated here, but that's because we went through a lot of work to make Rust safe. It is irritating when people claim that languages that didn't even try to do any of that work somehow did it better than Rust did.
That being said, Rust is definitely a language I will continue to study. I have no doubt it's making me a better programmer.
EDIT: I find it funny that I'm always defending C++ here. C++ is like that kid at school who's not really your friend but you always get stuck doing project with. You end up spending a lot of time with him, so when the time comes that you hear someone talk bad about him you feel obligated to point out that he's a actually pretty alright dude.