Bob said:
You can insist anything you like, and so can I ... but that doesn't
really make a difference about how things really are. So let's have
some more fun here...
It does if I take the address of an object (instance) of one of these
types or otherwise use it in some meaningful way.
If conditions are such and such, then something has to be done to make
the semantics work. No kidding!
References also become real storage objects under some circumstances.
Their address can be taken implicitly. If a struct or class contains a
reference, and I have a pointer to an instance of that struct or class,
then, indirectly, I have an address of that reference, (modulo some
displacement that may be zero).
So, references sometimes exist in the run-time environment as storage
objects in memory, and sometimes they disappear in the translation.
Exactly the same thing can be said about other kinds of values.
Fine with me if the assembly code shows that pointers
are used here. Why do YOU care so much, BTW?
Because I disagree with the blatant misinformation being pushed around
that references are not pointers. They aren't pointers in the type
system, but they are operationally equivalent to pointers and they are
real run-time objects except when optimized away.
As to reference members in a struct or class, I'd like to point out
that the standard disallows a pointer-to-member if that member has
reference type (hmm ... must be a good reason for that somewhere...).
It also disallows a regular pointer! If you take the address, you get
the target object.
Of course there is a good reason! References are not represented in the
type system.
There is no way you can even syntatically express pointer-to-reference
or pointer-to-member-reference. The type construction syntax isn't
valid.
But the reference is there in that object.
You also can't make pointers to bitfields. That doesn't mean they
aren't storage.
Exactly. Or it could reserve 0 bytes instead. So what?
It could not reserve zero bytes if the object cannot be optimized away.
If storage is needed for the reference, then it has to be large enough
to hold all the same information as a pointer.
Where do you get your strong guarantee that this statement is true?
In the perfectly well-formed, conforming code I can write to
demonstrate it.
extern int &ptr_to_ref(int *);
extern int *ref_to_ptr(int &);
int a = 42;
int *b = ref_to_ptr(a);
int &c = ptr_to_ref(b);
int *d = ref_to_ptr(c);
int &e = ptr_to_ref(d);
Now b, c, d and e all refer to a. Their initializing values were
smuggled through external functions, which can be trivially defined
like this:
int &ptr_to_ref(int *ptr) { return *ptr; }
int *ref_to_ptr(int &ref) { return &ref; }
The C++ standard supplies no such guarantee.
The C++ doesn't make guarantees; vendors do.
The C++ standard does require the above conversions to work.
It shows that we can freely convert between a pointer and a reference,
and that we can do this through an external data pathway: a blackbox
where we pass in a reference and get a pointer back or vice versa.
The only sticking point is that a null pointer can't be smuggled
through this conversion system because of the null dereference problem.
So there is one value that references don't have in their domain
compared to pointers. In practice, you can coax null references out of
your compiler. It's quite portable and harmless.
As a matter of fact,
there is even a paragraph *warning* us that it is unspecified whether
or not storage is allocated for a reference.
I have found it, it: "It is unspecified whether or not a reference
requires storage."
"Unspecified" is a normative term. It means that a choice must be made
among alternative requirements, without any documentation!
Why do you think that
sentence was deemed necessary by the standards committee?
That sentence says absolutely nothing.
It says that a choice must be made between among a set of alternatives
which exhaustively partition the space of all possibilities.
The same requirement (or absence of requirement) can be made simply by
omitting such a sentence.
If you erase the sentence, the implementor must still decide whether or
not a reference requires storage. And the implementor is still free not
to document anything about that choice.
The only situations when it's useful to explicitly say that something
is unspecified, rather than leave a lack of specification, is when it
might be mistakenly assumed that the behavior is specified, with dire
consequences.
A good example of this is the evaluation of function arguments. The
order is unspecified. If that isn't spelled out, implementors, or more
likely users of the language, might mistakenly assume that the order is
left to right.
It's very easy to write code which depends on that order of evaluation.
That code doesn't even have to invoke undefined behavior. For instance:
x(f(), g(), h());
results in the functions f, g and h being called in one of six possible
orders, each possibility being well-defined behavior.
So in other words, the behavior of a correct program can differ because
of this unspecified behavior. A correct test case can be written which
reports which way the choice went.
To write code that depends on whether or not a reference has storage,
you'd have to write some dubious construct, whose behavior is undefined
regardless of whether or not references have storage.
So it's not useful to spell this out at all.
How do you know? You are generalizing from specifics and have nothing
to back it up.
How do I know? Namely that the sentence I have written covers the
entire space of logical possibilities.
Either the object occupies run-time storage, or it doesn't.
P or not P
Always true!