I give up on this...
$5.2.7/2- "If T is a pointer type, v
shall be an rvalue of a pointer to
complete class type, and the result is
an rvalue of type T. If T is a
reference type, v shall be an lvalue
of a complete class type, and the
result is an lvalue of the type
referred to by T."
In accordance with the above, the following code should be well-formed.
struct A{};
struct B : A{};
int main(){
B b;
A a, &ar1 = b;
B& rb1 = dynamic_cast<B&>(ar1); // Does not $5.2.7/2 apply here?
B& rb2 = dynamic_cast<B&>(a); // and also here?
}
But it is not. All compilers complain about the operand to dynamic_cast not being polymorphic in accordance with
$5.2.7/6- Otherwise, v shall be a
pointer to or an lvalue of a
polymorphic type (10.3).
So my question is what does $5.2.7/2 mean? Why does $5.2.7/6 kick in here?
Well, all requirements in 5.2.7 should be observed together. You can't just stop after 5.2.7/2 and start writing code that supposedly satisfies everything "up to 5.2.7/2". The entire 5.2.7 defines the specification of dynamic_cast.
The polymorphic requirement is singled out because it is conditional. When you use dynamic_cast for upcasts, the polymorphic requirement does not apply (in fact, dynamic_cast is equivalent to static_cast in upcasts). The polymorphic requirement only applies when you use dynamic_cast for downcasts or crosscasts.
The specification of dynamic_cast is organized sequentially, meaning that it takes care of simpler cases first, and then proceeds to more complicated applications. You are supposed to read it step by step, until it covers your specific situation. Everything you read along that path applies cumulatively, And "otherwise" means: "if we haven't covered your case yet, then continue reading".
In order to do a downcast as in your example, Struct A needs to be polymorphic, and have RTTI. Here's an adjusted version that works, to a point:
struct A{virtual void f(){}};
struct B : A{};
int main(){
B b;
A a, &ar1 = b;
B& rb1 = dynamic_cast<B&>(ar1); // Does not $5.2.7/2 apply here?
//B& rb2 = dynamic_cast<B&>(a); // and also here?
}
By adding a virtual making it polymorphic, RTTI is enabled for the class, allowing downcasts.
Note that your second example cannot work - since you are casting a pod (a) to a reference to a pod - which is not allowed.
Update:
Your code is not allowed under 5.2.7.5 and is neither allowed under 5.2.7.6. My adjustment makes it work under 5.2.7.6
"Otherwise" in this case means, "unless the conditions in 5.2.7/5 apply".
You can tell this because /2 places a requirement on the program regarding the operand of the the dynamic_cast (note the "shall" language of "v shall be an lvalue" vs. the "is" language of "the result is an lvalue"). In common with other places in the standard, expressing a requirement doesn't necessarily mean that it's the only requirement. Other clauses can state extra requirements. In this case, /6 states an extra requirement that only applies in certain cases, depending on T and the static type of v.
/3, /4, /5 are telling you about the value of the result, and they're entirely consistent with the requirement in /2. None of them starts with "Otherwise". So to me it's fairly obvious that they do not form an "else if" chain starting at /2.
Some brackets or something might make this clearer (i.e. that the "otherwise" in /6 applies to the "if" in /5, and not to the "if" in /2, /3, or /4). But that's just not the house style.
Aside from anything else, the "otherwise" in /5 logically cannot meaningfully apply to the conditions in /2. /1 says that T must be "pointer or reference to complete class type, or cv void* ". /2 covers two cases - pointer types, and reference types. That's everything. There is no "otherwise" to /2 (unless it were to say "otherwise, a conforming compiler must issue a diagnostic", but that's implicit)
Related
There are three somewhat different concepts in c++:
Value category of an expression
lvalue, rvalue, etc.
I'm not able to formalize this, but roughly said, it's something that can often be relatively easy to deduce in a purely grammatical way. For example with "1.0" or "n" you often don't need any sort of knowledge of the context - you simply look at the actual symbols of the expression and can deduce the expression.
There are exceptions to this however - with f(x) for example we need to know whether f is a function or a type, and we also need to know whether it returns a value/reference type.
Type of an expression
An intuitive explanation of this would be "if this expression were to be evaluated, it would have some value. What would be the type of this value ?". Because it has to do with values, there can not be any sort of reference type - the type of an expression is a non reference type.
Unlike the above, this can easily require some sort of knowledge of the context - to deduce the type of "n" you need a symbol table that stores the type of "n".
Type of an object
This seems a bit more complicated, as an object is something that really only exists in the abstract, at runtime. While it's not something directly understandable as something in source code, regardless of the philosophical difference, we can say that whatever the object is, the type of the object is induced by a declaration expression
e.g. for int& x the object that the identifier x is associated with, is of type int&
//
1, Are the descriptions above correct in the sense that they at least roughly match how these concepts are described in the standard?
2, Assuming they are, how and in what situations are these properties used?
As an example, to resolve overloading, it seems to me that only the expression properties can be used for example - the relevant property of whether the object type is a reference or not is encapsulated in the expression value category to some extent, so we don't strictly need to know whether the return object is an lvalue reference type or not - the case when it is, is already contained in the definition of lvalue.
What are some examples of when the object type itself is used, rather than just the expression type?
I am mostly interested in this from the perspective of the standard, but a description on how it might differ from the compiler point of view will be also appreciated.
Value category of an expression
Type of an expression
Both of these require knowing not only the grammar of the expression used, which itself requires knowing whether names are types/templates (C++'s cannot be parsed without doing name lookup and understanding declarations), but also require knowing the value category and types of the operands of the expression. That's not only true for postfix expressions. It also applies e.g. to member access.
Type of an object
This is independent of the types and value categories of expressions as you said. All compile-time properties, such as overload resolution, are determined completely by value category and type of expressions. The result of an expression when evaluated (at runtime) may refer to some object (if it is a glvalue expression). The type of this object is not necessarily the same as the type of the expression and can vary between multiple evaluations of the same expression.
Usually the result of a glvalue expression of a given type is supposed to refer to an object of the same (up to qualifiers) type, but that is not generally guaranteed. Similarly a pointer value of a give pointed-to-type should be a pointer to an object of that same type (up to qualifiers), but that is not guaranteed. Basically the only way to violate these is however to use potentially dangerous casts such as reinterpret_cast.
A declaration int& x = /*...*/; doesn't declare or create any object. A reference is not considered an object. x will refer to the object which is the result of the right-hand side. As above, it may not necessarily have the type int. For example:
alignas(int) char y;
int& x = reinterpret_cast<int&>(y);
now the name x used in an expression has type int and value category lvalue, but the result of the lvalue expression refers to an object of type char (and access through this lvalue is not allowed because it would be an aliasing violation).
Objects are also not only created by declarations. As you said they are a runtime property that can only be talked about when considering a particular state of program execution. Other than from (non-reference) variables, objects can for example be created explicitly as temporary objects (via temporary materialization conversion from prvalues to xvalues), by new expressions or implicitly by certain operations which are defined to do so (e.g. a call to std::malloc under certain (strict) conditions).
It is also possible to reuse storage of objects, even from declared variables, or to nest objects in certain other objects. For example
static_assert(sizeof(float) == sizeof(int));
alignas(float) int x;
new(&x) float;
Now if the assertion succeeds after the new expression the int object from the declaration is not alive anymore and trying to read/write through x with
x = 1;
would have undefined behavior as x refers to an object outside its lifetime, but e.g.
*std::launder(reinterpret_cast<float*>(&x)) = 1;
will be fine after the new expression (but not before), because reinterpret_cast<float*>(&x) is a prvalue expression of type float* with a pointer value pointing to the out-of-lifetime int object, while the additional std::launder call adjusts the pointer value to point to the float object at the same storage location which is inside its lifetime.
(However you generally have to make sure that once the lifetime of the declared object would end normally, e.g. at the end of its scope, that there actually is an object of the correct type alive (and transparently-replacable with the original object) at the storage location. Otherwise you will have undefined behavior. Also, there is usually no practical reason for an example as above. One can simply declare two variables instead and have the compiler figure out whether the same storage can be reused.)
There is also the related concept of dynamic type of an expression, which however is slightly different from all of the above. Consider for example
struct A {
virtual int f() { return 1; }
};
struct B : A {
int f() override { return 2; }
};
B b;
A& a = b;
int x = a.f();
Here a in the last expression is an lvalue of type A and the result of the expression also refers to an object of type A, specifically the A base subobject of b, but the dynamic type of the expression for this specific evaluation is B, the most-derived type of the object to which the result of the expression a refers. This concept is used to determine which function override should be called in virtual dispatch but does not affect the (static) type or value category of the call expression a.f(). Using A& a = reinterpret_cast<A&>(b); instead would change the type of the result of the expression a to B and cause undefined behavior.
This line won't compile:
Shape shape = (i % 2) ? Circle(5) : Rectangle(5, 5);
(I know it's useless since whatever the expression returns will be reduced to a simple Shape, that's not the point).
Can't figure out why it won't compile. I'm creating a Shape variable named shape (which I think at this point creates a new Shape), and then I'm assigning this variable the result of an expression. Why doesn't this compile?
The error:
no match for ternary operator
What's really weird is that the longer code with exact same meaning does compile and run as expected:
Shape shape;
if (i % 2)
shape = Rectangle(5, 5);
else shape = Circle(5);
The detailed conversion rules for the conditional operator are rather complex (you can find the full quote from the standard in this answer if you are interested). The short of it is that, when used with objects of class type, it will attempt to convert its second operand to match the type of the third, and its third operand to match the type of the second, but it won't try to convert both to a third class type.
Since Circle isn't convertible to Rectangle and Rectangle isn't convertible to Circle, the compiler will complain (well, unless the two types define some odd conversion to pointer, scoped enumeration or arithmetic types, in which case §5.16 [expr.cond]/p5 comes into play).
Note also that your assignment will slice the object, which probably isn't a good idea.
The second and third operand of the conditional operator have to have a common type, as can be determined with the std::common_type trait. Interestingly perhaps, two classes derived from a common base class do not have that base class as a common type, nor are pointers or references thus related. Further thought quickly shows that indeed such a notion doesn't make sense: two classes can have any number of base classes in common, and there is no way in general to select a unique, preferred base.
If you want to use derived classes in the conditional operator, you have to cast the types yourself, explicitly.
A more realistic and sensible example of your code would be something like this:
Shape const & s = ask_user() ? static_cast<Shape const &>(show_me_a_circle())
: static_cast<Shape const &>(squares_all_the_way());
std::cout << "Your shape is " << s.get_colour() << ".\n";
When type types of the two branches in the conditional operator differ, one needs to be convertible to the other according to 5.16 [expr.cond] paragraph 16:
Otherwise, if the second and third operand have different types and either has (possibly cv-qualified) class type, or if both are glvalues of the same value category and the same type except for cv-qualification, an attempt is made to convert each of those operands to the type of the other. ...
The other cases referred to don't apply: they are about the types being void or one branch being a throw expression. The omission simply explains how the conversions are attempted and in which cases the conversion is chosen (essentially, if the type of one expression uniquely converts to the other but not the other way around).
Assuming your Circle or your Rectangle are convertible to Shape you can explicitly convert one or both expressions to Shape.
int main(){
int v = 1;
char* ptr = reinterpret_cast<char*>(&v);
char r = *ptr; //#1
}
In this snippet, the expression ptr point to an object of type int, as per:
expr.static.cast#13
Otherwise, the pointer value is unchanged by the conversion.
Indirection ptr will result in a glvalue that denotes the object ptr point to, as per
expr.unary#op-1
the result is an lvalue referring to the object or function to which the expression points.
Access an object by using a glvalue of the permitted type does not result in UB, as per
basic.lval#11
If a program attempts to access ([defns.access]) the stored value of an object through a glvalue whose type is not similar ([conv.qual]) to one of the following types the behavior is undefined:
a char, unsigned char, or std::byte type.
It seems it also does not violate the following rule:
expr#pre-4
If during the evaluation of an expression, the result is not mathematically defined or not in the range of representable values for its type, the behavior is undefined.
Assume the width of char in the test circumstance is 8 bits, its range is [-128, 127]. The value of v is 1. So, Does it mean the snippet at #1 does not result in UB?
As a contrast, given the following example
int main(){
int v = 2147483647; // or any value greater than 127
char* ptr = reinterpret_cast<char*>(&v);
char r = *ptr; //#2
}
#2 would be UB, Right?
It is the intention of the language that both snippets be implementation defined. I believe they were, until to C++17 which broke support for that language feature. See the defect report here. As far as I know, this has not been fixed in C++20.
Currently, the portable workaround for accessing memory representation is to use std::memcpy (example) :
#include <cstring>
char foo(int v){
return *reinterpret_cast<char*>(&v);
}
char bar(int v)
{
char buffer[sizeof(v)];
std::memcpy(buffer, &v, sizeof(v));
return *buffer;
}
foo is technically UB while bar is well defined. The reason is foo is UB is by omission. Anything the standard fails to define is by definition UB and the standard, in its current state, fails to define the behavior of this code.
bar produces the same assembly as foo with gcc 10. For simple cases, the actual copy is optimized out.
Regarding your rational, the reasoning seems sound except that, in my opinion, the rules defining unary operator* (expr.static.cast#13) doesn't have the effect you expect in this case. The pointer must point to the underlying representation, which is poorly defined as the linked defect describes. The fact that the pointer's value doesn't change does not mitigate the fact that it points to a different object. C++ allows objects to have the same address if their types are different, such as the first member in a standard layout class sharing the same address as the owning instance.
Note that the author is the defect report came to the same conclusion as you regarding snippet #1, but I disagree. But due to the fact that we are dealing with a language defect, and one that conflicts with state intentions, it is hard to definitively prove one behavior correct. The fundamental rules these arguments would be based on are known to be flawed in this particular case.
Does it mean the snippet at #1 does not result in UB?
Yes, the quoted rules mean that #1 is well defined.
#2 would be UB, Right?
No, as per the quoted rules, the behaviour of #2 is also well defined.
The type of ptr is char*, therefore the type of the expression *ptr is char whose value cannot exceed the value representable by char, thus expr#pre-4 does not apply.
Assume the width of char in the test circumstance is 8 bits, its range is [-128, 127].
This assumption is not necessary in order for #1 to be well defined.
The value of v is 1
This does not follow from the above assumption alone. It may be practically true in case of a little endian CPU (including the previous assumptions) although the standard doesn't specify the representation exactly.
First of all, I've seen this question about C99 and the accepted answer references operand is not evaluated wording in the C99 Standard draft. I'm not sure this answer applies to C++03. There's also this question about C++ that has an accepted answer citing similar wording and also In some contexts, unevaluated operands appear. An unevaluated operand is not evaluated. wording.
I have this code:
int* ptr = 0;
void* buffer = malloc( 10 * sizeof( *ptr ) );
The question is - is there a null pointer dereference (and so UB) inside sizeof()?
C++03 5.3.3/1 says The sizeof operator yields the number of bytes in the object representation of its operand. The operand is either an expression, which is not evaluated, or a parenthesized type-id.
The linked to answers cite this or similar wording and make use of "is not evaluated" part to deduce there's no UB.
However I cannot find where exactly the Standard links evaluation to having or not having UB in this case.
Does "not evaluating" the expression to which sizeof is applied make it legal to dereference a null or invalid pointer inside sizeof in C++?
I believe this is currently underspecified in the standard, like many issues such as What is the value category of the operands of C++ operators when unspecified?. I don't think it was intentional, like hvd points outs it is probably obvious to the committee.
In this specific case I think we have the evidence to show what the intention was. From GB 91 comment from the Rapperswil meeting which says:
It is mildly distasteful to dereference a null pointer as part of our specification, as we are playing on the edges of undefined behaviour. With the addition of the declval function template, already used in these same expressions, this is no longer necessary.
and suggested an alternate expression, it refers to this expression which is no longer in the standard but can be found in N3090:
noexcept(*(U*)0 = declval<U>())
The suggestion was rejected since this does not invoke undefined behavior since it is unevaluated:
There is no undefined behavior because the expression is an unevaluated operand. It's not at all clear that the proposed change would be clearer.
This rationale applies to sizeof as well since it's operands are unevaluated.
I say underspecified but I wonder if this is covered by section 4.1 [conv.lval] which says:
The value contained in the object indicated by the lvalue is the rvalue result. When an lvalue-to-rvalue conversion occurs
within the operand of sizeof (5.3.3) the value contained in the referenced object is not accessed, since that operator
does not evaluate its operand.
It says the value contained is not accessed, which if we follow the logic of issue 232 means there is no undefined behavior:
In other words, it is only the act of "fetching", of lvalue-to-rvalue conversion, that triggers the ill-formed or undefined behavior
This is somewhat speculative since the issue is not settled yet.
Since you explicitly asked for standard references - [expr.sizeof]/1:
The operand is either an expression, which is an unevaluated operand
(Clause 5), or a parenthesized type-id.
[expr]/8:
In some contexts, unevaluated operands appear (5.2.8, 5.3.3, 5.3.7,
7.1.6.2). An unevaluated operand is not evaluated.
Because the expression (i.e. the dereferenciation) is never evaluated, this expression is not subject to some constraints that it would normally be violating. Solely the type is inspected. In fact, the standard uses null references itself in an example in [dcl.fct]/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);
— end note ]
The specification only says that dereferencing some pointer that is NULL is UB. Since sizeof() is not a real function, and it doesn't actually use the arguments for anything other than getting the type, it never references the pointer. That's WHY it works. Someone else can get all the points for looking up the spec wording that states that "the argument to sizeof doesn't get referenced".
Note that it's also entirely legal to do int arr[2]; size_t s = sizeof(arr[-111100000]); too - it doesn't matter what the index is, because sizeof never actually "does anything" to the argument passed.
Another example to show how it's "not doing anything" would be something like this:
int func()
{
int *ptr = reinterpret_cast<int*>(32);
*ptr = 7;
return 42;
}
size_t size = sizeof(func());
Again, this wouldn't crash, because func() is just resolved by the compiler to the type that it produces.
Equally, if sizeof actually "does something" with the argument, what would happen when you do this:
char *buffer = new sizeof(char[10000000000]);
Would it create a 10000000000 stack allocation, then give the size back after it crashed the code because there isn't enough megabytes of stack? [In some systems, stack size is counted in bytes, not megabytes]. And whilst nobody writes code like that, you could easily come up with something similar using typedef of either buffer_type as an array of char, or some kind of struct with large content.
Recently tried the following program and it compiles, runs fine and produces expected output instead of any runtime error.
#include <iostream>
class demo
{
public:
static void fun()
{
std::cout<<"fun() is called\n";
}
static int a;
};
int demo::a=9;
int main()
{
demo* d=nullptr;
d->fun();
std::cout<<d->a;
return 0;
}
If an uninitialized pointer is used to access class and/or struct members behaviour is undefined, but why it is allowed to access static members using null pointers also. Is there any harm in my program?
TL;DR: Your example is well-defined. Merely dereferencing a null pointer is not invoking UB.
There is a lot of debate over this topic, which basically boils down to whether indirection through a null pointer is itself UB.
The only questionable thing that happens in your example is the evaluation of the object expression. In particular, d->a is equivalent to (*d).a according to [expr.ref]/2:
The expression E1->E2 is converted to the equivalent form
(*(E1)).E2; the remainder of 5.2.5 will address only the first
option (dot).
*d is just evaluated:
The postfix expression before the dot or arrow is evaluated;65 the
result of that evaluation, together with the id-expression, determines
the result of the entire postfix expression.
65) If the class member access expression is evaluated, the subexpression 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.
Let's extract the critical part of the code. Consider the expression statement
*d;
In this statement, *d is a discarded value expression according to [stmt.expr]. So *d is solely evaluated1, just as in d->a.
Hence if *d; is valid, or in other words the evaluation of the expression *d, so is your example.
Does indirection through null pointers inherently result in undefined behavior?
There is the open CWG issue #232, created over fifteen years ago, which concerns this exact question. A very important argument is raised. The report starts with
At least a couple of places in the IS state that indirection through a
null pointer produces undefined behavior: 1.9 [intro.execution]
paragraph 4 gives "dereferencing the null pointer" as an example of
undefined behavior, and 8.3.2 [dcl.ref] paragraph 4 (in a note) uses
this supposedly undefined behavior as justification for the
nonexistence of "null references."
Note that the example mentioned was changed to cover modifications of const objects instead, and the note in [dcl.ref] - while still existing - is not normative. The normative passage was removed to avoid commitment.
However, 5.3.1 [expr.unary.op] paragraph 1, which describes the unary
"*" operator, does not say that the behavior is undefined if the
operand is a null pointer, as one might expect. Furthermore, at least
one passage gives dereferencing a null pointer well-defined behavior:
5.2.8 [expr.typeid] paragraph 2 says
If the lvalue expression is obtained by applying the unary * operator
to a pointer and the pointer is a null pointer value (4.10
[conv.ptr]), the typeid expression throws the bad_typeid exception
(18.7.3 [bad.typeid]).
This is inconsistent and should be cleaned up.
The last point is especially important. The quote in [expr.typeid] still exists and appertains to glvalues of polymorphic class type, which is the case in the following example:
int main() try {
// Polymorphic type
class A
{
virtual ~A(){}
};
typeid( *((A*)0) );
}
catch (std::bad_typeid)
{
std::cerr << "bad_exception\n";
}
The behavior of this program is well-defined (an exception will be thrown and catched), and the expression *((A*)0) is evaluated as it isn't part of an unevaluated operand. Now if indirection through null pointers induced UB, then the expression written as
*((A*)0);
would be doing just that, inducing UB, which seems nonsensical when compared to the typeid scenario. If the above expression is merely evaluated as every discarded-value expression is1, where is the crucial difference that makes the evaluation in the second snippet UB? There is no existing implementation that analyzes the typeid-operand, finds the innermost, corresponding dereference and surrounds its operand with a check - there would be a performance loss, too.
A note in that issue then ends the short discussion with:
We agreed that the approach in the standard seems okay: p = 0; *p;
is not inherently an error. An lvalue-to-rvalue conversion would give
it undefined behavior.
I.e. the committee agreed upon this. Although the proposed resolution of this report, which introduced so-called "empty lvalues", was never adopted…
However, “not modifiable” is a compile-time concept, while in fact
this deals with runtime values and thus should produce undefined
behavior instead. Also, there are other contexts in which lvalues can
occur, such as the left operand of . or .*, which should also be
restricted. Additional drafting is required.
…that does not affect the rationale. Then again, it should be noted that this issue even precedes C++03, which makes it less convincing while we approach C++17.
CWG-issue #315 seems to cover your case as well:
Another instance to consider is that of invoking a member function
from a null pointer:
struct A { void f () { } };
int main ()
{
A* ap = 0;
ap->f ();
}
[…]
Rationale (October 2003):
We agreed the example should be allowed. p->f() is rewritten as
(*p).f() according to 5.2.5 [expr.ref]. *p is not an error when
p is null unless the lvalue is converted to an rvalue (4.1
[conv.lval]), which it isn't here.
According to this rationale, indirection through a null pointer per se does not invoke UB without further lvalue-to-rvalue conversions (=accesses to stored value), reference bindings, value computations or the like. (Nota bene: Calling a non-static member function with a null pointer should invoke UB, albeit merely hazily disallowed by [class.mfct.non-static]/2. The rationale is outdated in this respect.)
I.e. a mere evaluation of *d does not suffice to invoke UB. The identity of the object is not required, and neither is its previously stored value. On the other hand, e.g.
*p = 123;
is undefined since there is a value computation of the left operand, [expr.ass]/1:
In all cases, the assignment is sequenced after the value computation
of the right and left operands
Because the left operand is expected to be a glvalue, the identity of the object referred to by that glvalue must be determined as mentioned by the definition of evaluation of an expression in [intro.execution]/12, which is impossible (and thus leads to UB).
1 [expr]/11:
In some contexts, an expression only appears for its side effects.
Such an expression is called a discarded-value expression. The
expression is evaluated and its value is discarded. […]. The lvalue-to-rvalue conversion (4.1) is
applied if and only if the expression is a glvalue of
volatile-qualified type and […]
From the C++ Draft Standard N3337:
9.4 Static members
2 A static member s of class X may be referred to using the qualified-id expression X::s; it is not necessary to use the class member access syntax (5.2.5) to refer to a static member. A static member may be referred
to using the class member access syntax, in which case the object expression is evaluated.
And in the section about object expression...
5.2.5 Class member access
4 If E2 is declared to have type “reference to T,” then E1.E2 is an lvalue; the type of E1.E2 is T. Otherwise,
one of the following rules applies.
— If E2 is a static data member and the type of E2 is T, then E1.E2 is an lvalue; the expression designates the named member of the class. The type of E1.E2 is T.
Based on the last paragraph of the standard, the expressions:
d->fun();
std::cout << d->a;
work because they both designate the named member of the class regardless of the value of d.
runs fine and produces expected output instead of any runtime error.
That's a basic assumption error. What you are doing is undefined behavior, which means that your claim for any kind of "expected output" is faulty.
Addendum: Note that, while there is a CWG defect (#315) report that is closed as "in agreement" of not making the above UB, it relies on the positive closing of another CWG defect (#232) that is still active, and hence none of it is added to the standard.
Let me quote a part of a comment from James McNellis to an answer to a similar Stack Overflow question:
I don't think CWG defect 315 is as "closed" as its presence on the "closed issues" page implies. The rationale says that it should be allowed because "*p is not an error when p is null unless the lvalue is converted to an rvalue." However, that relies on the concept of an "empty lvalue," which is part of the proposed resolution to CWG defect 232, but which has not been adopted.
The expressions d->fun and d->a() both cause evaluation of *d ([expr.ref]/2).
The complete definition of the unary * operator from [expr.unary.op]/1 is:
The unary * operator performs indirection: the expression to which it is applied shall be a pointer to an object type, or a pointer to a function type and the result is an lvalue referring to the object or function to which the expression points.
For the expression d there is no "object or function to which the expression points" . Therefore this paragraph does not define the behaviour of *d.
Hence the code is undefined by omission, since the behaviour of evaluating *d is not defined anywhere in the Standard.
What you are seeing here is what I would consider an ill-conceived and unfortunate design choice in the specification of the C++ language and many other languages that belong to the same general family of programming languages.
These languages allow you to refer to static members of a class using a reference to an instance of the class. The actual value of the instance reference is of course ignored, since no instance is required to access static members.
So, in d->fun(); the the compiler uses the d pointer only during compilation to figure out that you are referring to a member of the demo class, and then it ignores it. No code is emitted by the compiler to dereference the pointer, so the fact that it is going to be NULL during runtime does not matter.
So, what you see happening is in perfect accordance to the specification of the language, and in my opinion the specification suffers in this respect, because it allows an illogical thing to happen: to use an instance reference to refer to a static member.
P.S. Most compilers in most languages are actually capable of issuing warnings for that kind of stuff. I do not know about your compiler, but you might want to check, because the fact that you received no warning for doing what you did might mean that you do not have enough warnings enabled.