To 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.
You changed the vector from a mutable one to an immutable one. That isn't the same code. What if you need a mutable vector?
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++.
Yes, if you make a dangling pointer factory or introduce global state then you can break things. Luckily you wouldn't use anything like that to write the safe and performant data structures I'm talking about.
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.
> Luckily you wouldn't use anything like that to write the safe and performant data structures I'm talking about.
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"!
Highjack the term safety, use contrived examples, and slippery slope arguments all you want. You know exactly the advantage I'm talking about.
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.
It's not "hijacking" the term "safety"--it's having a precise definition of it. It's not a slippery slope to show both examples of unsafety and to show that this lack of safety leads to security problems in the real world.
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.
Rust is CLEARLY better at safety than C++. Stop seeking validation for that. Everyone who knows about Rust is convinced. I'm sorry it must be frustrating talking to someone emotionally disinterested about the whole thing. I've just been thinking about C++'s advantage in that particular instance from a software architecture point of view and you kind of failed to persuade me otherwise.
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.
It seems you're sort of missing a big point here. You demonstrate how, with sufficient vigilance, you can write safe C++. The goal of Rust is to move the vigilance into the language and make the unsafety opt-in rather than attempt-to-run-from.
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.
> So now using const is some kind of difficult task? Not much cognitive overhead there. I thought everyone was onboard with immutability these days.
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++.
A "factory function" is just a function. Unless you have some way of statically distinguishing "factory functions" from functions, you are now arguing "you can write safe C++ if you make all your data immutable and you don't use functions". Besides the fact that this is not true (consider the destruction order for rvalues), how is this a statement that is possibly relevant to the real world?
To 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.
EDIT: Down voted for legitimate rebuttal. Nice.