3 ms·
That wouldn't compile. 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 s
by autoreleasepool 11y ago
That wouldn't compile.
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.
EDIT: Down voted for legitimate rebuttal. Nice.
- oldmanjay 11y agoIt 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.
- autoreleasepool 11y ago> with sufficient vigilance 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.
- pcwalton 11y ago> 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++.
- autoreleasepool 11y ago> 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.
- pcwalton 11y agoA "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?
- deleted 11y ago[deleted]
- pcwalton 11y agoYou 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++.
- autoreleasepool 11y agoYes, 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.
- pcwalton 11y ago> 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"!
- 11y ago