From an implementation perspective, there is one very important difference between a pointer and a reference in C++: references cannot be null.
This is undefined behaviour:
int* px = nullptr; int &rx = *px; This doesn't make a difference except in one specific scenario: casting in the presence of multiple inheritance.
Consider this:
struct base1 { uint64_t member1; }; struct base2 { uint64_t member2; }; struct derived : public base1, base2 { }; What code should this function compile to?
base2& cast_from_derived_to_base2_ref(derived& pd) { return (base2&)pd; } If you said "basically add 8", you would be correct. Clang 16.0.0 on x64 at -O2 generates this code:
cast_from_derived_to_base2_ref(derived&): lea rax, [rdi + 8] ret Now, what code should this function compile to?
base2* cast_from_derived_to_base2_ptr(derived* pd) { return (base2*)pd; } If you said "the same code", you would be wrong. It would give an incorrect answer if pd were a null pointer; in that case, this function should also return a null pointer.
This is the code that Clang 16.0.0 generates:
cast_from_derived_to_base2_ptr(derived*): lea rax, [rdi + 8] test rdi, rdi cmove rax, rdi ret Of course, there are also high-level semantic differences (references are immutable, for example), but this is the key difference in implementation.
Having said all that, implementing a reference as a value rather than a pointer would be an optimisation. The main barrier would be escape analysis; the compiler would have to be convinced that the location referred to isn't visible from outside the current function (e.g. another thread).