Re: what's the difference?
On Apr 20, 12:48 pm, "marcin.sfi...@gmail.com"
<marcin.sfi...@gmail.com> wrote:
void function(MyClass& obj) {
assert (&obj != nullptr);
obj.method();
}
No, please don't do that. There is no possibility because anyone would
pass a "null reference" to such a function, because even if he does,
the mistake is already at the point of dereference (i.e. operator*),
and not at the call.
Okay, my bad. This idea was based on implementations I used, not on
standard. What I wanted to say was that a reference is not in some
magical way always referencing to an object and that the programmer
should now this.
I personally do not use such assert, mainly because I do not use
references to pass arguments in all cases. I think that the real
problem here is that the new programmers are told to use references
to pass arguments because it is safer. This is IMO totaly untrue
It is safer, in a sense that using a reference is a very clear,
unambiguous, and universally understood way of indicating "non-null".
(or just on implementations I used). Passing null pointer to a
function (wich is documented not to accept null pointers)
and dereferencing null pointer to pass it by reference are both
errors done by programmer using the function. However in the
first case the function can check if the argument is correct
and in the second (w.r.t. standrad) it cannot.
The function _can_ check the reference "for null", in the manner your
code sample demonstrates. It does not become non-conforming because of
that. The calling code, however, does become non-conforming the moment
it tries to dereference null. But, of course, there are many places
where this can happen in C++, so avoiding functions with reference
arguments for this sole reason won't help much.
There's one other thing about references in this context. Since, as
far as compiler is concerned, there are no null references, it doesn't
need to do any special checks for them when doing upcasts; and, IIRC,
at least g++ does just that. To understand what I mean, a brief side
excursion into implementation details. Let's say we have this class
hierarchy:
struct Base1 { int i; }
struct Base2 { int j; }
struct Derived : Base1, Base2 { };
Now, in a typical straightforward implementation, a Base2 subobject
within an instance of Derived is at a non-zero offset from beginning.
So, to perform an upcast from Derived* to Base2* (or from Derived& to
Base2&), the compiler will need to generate an ADD instruction (or
some equivalent). However, for pointers, it has to account for the
fact that a pointer can be null (which is typically all bits 0), in
which case the addition must not be performed (since casting a null
Derived* to Base2* should yield null Base2*, and adding an offset to 0
will give a non-zero value). For references, however, the compiler
needs not perform such a check, because they are never null. So, for
example, this may happen:
void foo(Base2& b)
{
assert(&b != 0);
...
}
Derived* d = 0;
foo(*d); // no assert!
Of course, this still isn't something that's guaranteed to happen - as
far as Standard is concerned, the above is another manifestation of
U.B. For the record, MSVC does do null checks on references.
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]