In C++, given ST *p
, *p
produces an lvalue (expr.unary.op#1), and if the static type ST
is primitive converting *p
to an rvalue actually accesses and reads *p
(conv.lval, basic.lval#11). But suppose we don't convert *p
to an rvalue. We can evaluate &*p
, or bind a reference to *p
, etc. p
must still point to an object or function of dynamic type DT
.
Must ST
be somehow related to DT
? Strictly speaking, rules &*p
might even require ST = DT
, but that's absurd because it's more restrictive than rules about actual accesses! Quoting expr.unary.op#1:
The unary
*
operator performs indirection. Its operand shall be a prvalue of type “pointer toT
”, whereT
is an object or function type. The operator yields an lvalue of typeT
denoting the object or function to which the operand points.
Hence the title question: Must lvalues of type T
point to objects of type T
? "Clearly not", I think, but does the standard answer this anywhere?
I expect &*p
must be less restrictive than accessing *p
, and can be more restrictive than evaluating p
— and those cases have clearer rules:
- accessing pointers has strict requirements ("strict aliasing"): actual accesses are only defined behavior if static type
ST
belongs to a certain list of types; this list includes more than dynamic typeDT
; specifically,ST
can beDT
, its signed/unsigned variants, andchar
,unsigned char
, andstd::byte
(basic.lval#11). - pointers themselves have no such requirement: if we just evaluate pointer
p
itself, static typeST
and dynamic typeDT
need not be related, because astatic_cast
fromvoid*
to an arbitrary type has defined behavior if alignment requirements are satisfied (expr.static.cast#14).
Note: according to https://stackoverflow.com/a/21053756/53974, it seems (only?) expected that p
points to an object or function.
EDIT: For concreteness, consider function f
below.
#include <iostream>
short* f(int *ip) { // assume `p` actually points to DT = int
void *vp = ip;
// ^^ legal, per https://eel.is/c++draft/conv.ptr#2
short* sp = static_cast<short*> (vp);
// legal, per [expr.static.cast#14]
return &*sp;
}
int main(int argc, char ** argv) {
int i = 0;
short* sp = f(&i);
void* vp = sp;
int* ip = static_cast<int*>(vp);
*ip = 1;
std::cout << "I: " << i << std::endl;
return 0;
}