Setup
Given this user-defined type:
struct T
{
static int x;
int y;
T() : y(38);
};
and the requisite definition placed somewhere useful:
int T::x = 42;
the following is the canonical way to stream the int
's value to stdout:
std::cout << T::x;
Control
Meanwhile, the following is (of course) invalid due to an instance of T
not existing:
T* ptr = NULL; // same if left uninitialised
std::cout << ptr->y;
Question
Now consider the horrid and ev开发者_如何学JAVAil and bad following code:
T* ptr = NULL;
std::cout << ptr->x; // remember, x is static
Dereferencing ptr
is invalid, as stated above. Even though no physical memory dereference takes place here, I believe that it still counts as one, making the above code UB. Or... does it?
14882:2003 5.2.5/3 states explicitly that a->b
is converted to (*(a)).b
, and that:
The postfix expression before the dot or arrow is evaluated; This evaluation happens even if the result is unnecessary to determine the value of the entire postfix expression, for example if the id-expression denotes a static member.
But it's not clear whether "evaluation" here involves an actual dereference. In fact neither 14882:2003 nor n3035 seem to explicitly say either way whether the pointer-expression has to evaluate to a pointer to a valid instance when dealing with static members.
My question is, just how invalid is this? Is it really specifically prohibited by the standard (even though there's no physical dereference), or is it just a quirk of the language that we can probably get away with? And even if it is prohibited, to what extent might we expect GCC/MSVC/Clang to treat it safely anyway?
My g++ 4.4 appeared to produce code that never attempts to push the [invalid] this
pointer onto the stack, with optimisations turned off.
BTW If T
were polymorphic then that would not affect this, as static members cannot be virtual.
it's not clear whether "evaluation" here involves an actual dereference.
I read "evaluation" here as "the subexpression is evaluated." That would mean that the unary *
is evaluated and you perform indirection via a null pointer, yielding undefined behavior.
This issue (accessing a static member via a null pointer) is discussed in another question, When does invoking a member function on a null instance result in undefined behavior? While it discusses member functions specifically, I don't see any reason that data members are any different in this respect. There is some good discussion of the issue there.
There was a defect reported against the C++ Standard that asks "Is call of static member function through null pointer undefined?" (see CWG Defect 315) This defect is closed and its resolution states that it is valid to call a static member function via a null pointer:
p->f()
is rewritten as(*p).f()
according to 5.2.5 [expr.ref].*p
is not an error whenp
is null unless the lvalue is converted to an rvalue
However, this resolution is in fact wrong.
It presupposes the concept of an "empty lvalue," which is part of the proposed resolution for another defect, CWG defect 232, which asks the more general question, "Is indirection through a null pointer undefined behavior?"
The resolution to that defect would make certain forms of indirection through a null pointer (like calling a static member function) valid. However, that defect is still open and its resolution has not been adopted into the C++ Standard. Until that defect is closed and its resolution is incorporated into the C++ Standard, indirection via a null pointer (or dereferencing a null pointer, if one prefers that term) always yields undefined behavior.
Concerning p->a, where p is a null pointer, and a a static data member: §9.4/2 says "A static member may be referred to using the class member access syntax, in which case the object-expression is evaluated." (The "object-expression" is the expression to the left of the . or the ->.)
Looking from computer side onto OOP it is yet another way to calculate where data resides in memory. When data is not static - it is calculated from instance pointer, when data is static it is always calculated as fixed pointer in data segment. Template adds nothing, since resolved at compile time.
So it is rather popular technique to use NULL as start pointer (for example evaluate offset of filed in class for persisting purposes)
So code above is correct for static data.
精彩评论