12
class Foo { public: static const int kType = 42; }; void Func() { Foo *bar = NULL; int x = bar->kType; putc(x, stderr); } 

Is this defined behavior? I read through the C++ standard but couldn't find anything about accessing a static const value like this... I've examined the assembly produced by GCC 4.2, Clang++, and Visual Studio 2010 and none of them perform a dereference of the NULL pointer, but I'd like to be sure.

1
  • 1
    Most compilers should give you a warning about accessing static/const members through an instance pointer. Commented Aug 17, 2010 at 0:28

5 Answers 5

10

You can use a pointer (or other expression) to access a static member; however, doing so through a NULL pointer unfortunately is officially undefined behavior. From 9.4/2 "Static members":

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.

Based on the example that follows:

class process { public: static void reschedule(); }; process& g(); void f() { process::reschedule(); // OK: no object necessary g().reschedule(); // g() is called } 

The intent is to allow you to ensure that functions will be called in this scenario.

Sign up to request clarification or add additional context in comments.

9 Comments

I was going to split a hair and point out that "evaluating the object expression" doesn't necessarily imply dereferencing the pointer -- it could just mean finding the pointer value -- but looking at §5.2.5, it appears that it does, because the "object-expression" in this case is defined to be "*bar", not just "bar". So yer right.
Hasn't this been a subject of debate? The notion of evaluating an object, as in simply nominating it, is rather zen. On the other hand, the example under that text clearly shows a function call. Clearly they mean that the expression before the "." or "->" is evaluated.
@Potatoswatter: I believe this my interpretation is correct, but I certainly don't consider myself an authority. While I think that the likelihood of calling a static member function through an appropriately typed NULL pointer is likely to work as you might expect, it's easy enough to have the call be made using the type name rather than a pointer (though I think for templates that might not always be true), so you might be better off simply not using pointers for this.
Also, note that there might be a style issue that makes it so you should be reluctant to call static member functions through object-expressions: stackoverflow.com/questions/840522/…
@Michael: Yeah. I'm trying to find where the standard says that dereferencing NULL is bad. The specification of * at 5.3.1 only says that it turns a pointer into an lvalue reference, and that "the expression to which it is applied shall be a pointer to an object type". I'm not sure what "type" means. Does it mean the expression must be a valid pointer else UB, or it only needs pointer type? Hmm. Anyway, of course you're right. Obtaining an invalid/NULL pointer generally implies having the type name on hand. I guess if you had a function returning a NULL pointer to template type…
|
3

I believe that the actual value of the type is not used at all when calling

bar->kType 

since kType is static, and bar is of type Foo it is the same as calling

Foo::kType 

which you should really be doing anyway for clarity.

Calling bar->kType gives a compiler warning on most platforms for this reason.

4 Comments

+1 "which you should really be doing anyway for clarity"
Correct, static members are stored independently of object instances, so the object itself is never dereferenced.
@casablanca: That's an implementation detail. If you have documented proof that this is the behaviour of your particular compiler version then great! Otherwise, don't do it. Certainly, in terms of a question about the abstract machine called C++, this isn't a "correct" answer I'm afraid.
@LightnessRacesinOrbit I was not condoning the use of this idiom. Specifically I was urging against it in favour of "Foo::kType". I was simply trying to explain to the OP why it doesn't fail on their implementation, something which the existing answer had not explained at the time.
1

Apart from the issue about accessing through the NULL pointer, there is another subtle issue in the code

$9.4.2/2 - "The declaration of a static data member in its class definition is not a definition and may be of an incomplete type other than cv-qualified void. The definition for a static data member shall appear in a namespace scope enclosing the member’s class definition."

$9.4.2/4- "If a static data member is of const integral or const enumeration type, its declaration in the class definition can specify a constant-initializer which shall be an integral constant expression (5.19). In that case, the member can appear in integral constant expressions. The member shall still be defined in a namespace scope if it is used in the program and the namespace scope definition shall not contain an initializer."

class Foo { public: static const int kType = 42; }; int const Foo::kType; void Func() { Foo *bar = NULL; int x = bar->kType; putc(x, stderr); } 

So, yet one more reason for UB in the OP code.

Comments

1

Even if it worked it is awful code.

In serious programming you code not only for yourself, but also for others who will maintain your code. Playing tricks like this must be avoided, because you respect your colleagues.

One consequence of this code: whether the pointer is NULL or not is even not at question, but it implies that this member kType may not be a plain non-static member of the class. Sometimes classes are big (this is evil too) and one cannot always recheck the definition of each and every variable.

Be rigorous. And call all your static members only this way:

Foo::kType 

Another possibility is to follow a coding convention that let know that the member is static, for example, a s_ prefix for all classes static members:

Foo::s_kType 

Comments

-1

There is a higher rule so to speak which basically says - don't even think about compiling things that are provably not used. Advanced template programming depends on this a lot, so even if it might be a bit gray-zonish when a compiler clearly sees that the result of a construct is not used it's just going to eliminate it. Especially when it's provably safe like in this case.

You may want to try a few variants if you want - like making pointer a param of a function, result of a function, leaving a pointer uninitialized (best chance for triggering compiler complaint), doing a straight cast of 0 (best chance of being conplaint-free).

1 Comment

That's silly. All manner of optimisations could result in your function call not even happening, or other crazy stuff. I'm serious. This can happen in practice.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.