The purpose of this construct is to emulate a fake unnamed object of type SomeType
in situations when you formally need an object, but don't want or can't declare a real one. It has its valid uses and does not necessarily cause undefined behavior.
A classic example would be determining the size of some class member
sizeof (*(SomeClass *) 0).some_member
or a similar application of decltype
decltype((*(SomeClass *) 0).some_member)
Neither of the above examples causes any undefined behavior. In non-evaluated context expressions like *(SomeClass *) 0
are perfectly legal and valid.
You can also see this technique used for illustrative purposes in the language standard itself, as in 8.3.5/12
A trailing-return-type is most useful for a type that would be more
complicated to specify before the declarator-id:
template <class T, class U> auto add(T t, U u) -> decltype(t + u);
rather than
template <class T, class U> decltype((*(T*)0) + (*(U*)0)) add(T t, U u);
Observe how the (*(T*)0) + (*(U*)0)
expression is used under decltype
to perform compile-time prediction of the result type of binary +
operator between types T
and U
.
Of course, again, such tricks are only valid when used in non-evaluated contexts, as shown above.
Sometimes it is used as an initializer for "null references" as in
SomeType &r = *(SomeType *) 0;
but this actually crosses the boundary of what's legal and produces undefined behavior.
What you have in your specific example is invalid, since it attempts to access an invalid "null lvalue" in evaluated context.
P.S. In C language there's also that peculiar part of specification that says that operators &
and *
cancel each other, meaning that &*(SomeType *) 0
is valid and guaranteed to evaluate to null pointer. But it does not extend to C++.