void test() {
int* p = 0;
int& r = *p;
}
compiles without complaint. void test() {
int* p = 0;
int& r = *p;
}
compiles without complaint.But if you do
int& r = *((int*)0);
You'll find it will warn, and tell you that what you're doing is illegal:<source>:2:14: warning: binding dereferenced null pointer to reference has undefined behavior [-Wnull-dereference]
Same if you try to naively return nullptr on a method that returns a reference:
int& iref() {
return nullptr;
}
<source>: In function 'int& iref()':<source>:6:12: error: invalid initialization of non-const reference of type 'int&' from an rvalue of type 'std::nullptr_t'
6 | return nullptr;
| ^~~~~~~
Compiler returned: 1If you get a null reference, this is by chance and not by design.
Although no sane implementation would do this.
And that would be perfectly compiler-legal, and your code would have one free bug.
As you've shown, references can be null, but theyre not supposed to be and are assumed nearly universally to not be.
The argument comes down to when the undefined behavior occurs: is it at the deference to create the reference, or is it on the first memory access using the reference? The language pedants will say the former, but in practice, it's the latter.
In practice, you'll likely be able to invoke a member function on a null pointer or reference, as long as that member doesn't directly or indirectly access data members, or virtual functions of the type. Obligatory, I dont recommend doing this or relying upon this behavior, it's just behavior I've seen in my 2 decades of debugging C++.
It doesn't mean a method that takes or returns a pointer must allow null as a valid value, of course, Optional<> is better for that. But standards-enforced non-null is a very practically useful aspect of references that differ them from pointers.
The undefined behavior is always when the null reference is created. The issue will _usually_ manifest when you try to dereference the pointer, but the undefined behavior was creating the null reference in the first place.