5

I am working on some code where there is a simple enum in a class. A different piece of code has a pointer to that class and is accessing a value of the enum through the arrow pointer.

How on earth is the class able to access MY_VALUE1 this way? I though it would only allow access via MyClass::MY_VALUE1 or MyClass::MyEnum::MY_VALUE1.

class MyClass {
public:
enum MyEnum{
    MY_VALUE0 = 0,
    MY_VALUE1 = 1
};
//getters, setters as appropriate
};

//Other class
MyClass* myClass = new MyClass();

//Compiles without C++11
if(getRandomEnum() == myClass->MY_VALUE1)
{
    //Do Stuff
}
Gandalf458
  • 2,139
  • 1
  • 21
  • 36
  • 2
    For the guy who downvoted, at least give an explanation as to why. – Syntactic Fructose Nov 05 '13 at 17:56
  • I've a suspicion the answer will be closely related to similar syntactic sugar used when invoking a static class method using the same mechanism. Its an issue of visibility, and though I don't have the standard in front of me, I'm sure its covered somewhere. – WhozCraig Nov 05 '13 at 18:25
  • I don't have the standard or even an IDE to hand, but I would guess that the values in an embedded enum are sort of considered static const members. (I believe that if you can successfully access them from from a null pointer, they *are* static) – Grimm The Opiner Nov 05 '13 at 18:26
  • @user1158692 using a null pointer to do it is implementation-defined at best, outright undefined behavior by standard. I remember seeing Don Box do it once in a VC++ dev conference about 12 years ago, too lazy to expand an ATL template `CComObject::CreateInstance` invoke, instead using `CComObject *p=NULL; p->CreateInstance(&p);` When called on it, his response was essentially "it works with MS VC++, so bug off". – WhozCraig Nov 05 '13 at 18:29
  • @user1158692 You can access it via a null pointer, I've just tried it with Apple clang 5.0 (based on LLVM-3.3 svn). – trojanfoe Nov 05 '13 at 18:30
  • Related questions on static class access via invalid pointer can be found [here](http://stackoverflow.com/questions/1524312/why-am-i-able-to-make-a-function-call-using-an-invalid-class-pointer), and [here](http://stackoverflow.com/questions/2505328/calling-class-method-through-null-class-pointer), and I'm sure there are others. – WhozCraig Nov 05 '13 at 18:33
  • That isn't really "accessing via a pointer". It's accessing a _constant_, which happens to be within a class definition's namespace. You're just giving the compiler the necessary information to find the constant. – Damon Nov 05 '13 at 18:34
  • @trojanfoe It's undefined behavior if the pointer is null (although it's likely that you'll get away with it). – James Kanze Nov 05 '13 at 18:35
  • @WhozCraig It seems very odd to do things that way. Thank you for helping me to see that when it comes to class data members, they can be accessed with essentially anything that references the class. – Gandalf458 Nov 05 '13 at 18:56
  • @JamesKanze After a bit of reading of the standard, Section 5.5.4 says that "if the dynamic type of the object expression does not contain the member to which the pointer refers, the behavior is undefined". Because the enum is a part of the class and not an instantiated member variable, the behavior is, according to my interpretation, defined. – Gandalf458 Nov 05 '13 at 21:27
  • @Gandalf458 The expression `p->enumValue` is formally defined to mean `(*p).enumValue`. If `p` is a null pointer, the expression `*p` has undefined behavior, at least if it is evaluated. §5.2.5 states clearly that it is evaluated, even if the results are not required to evaluate the complete expression. – James Kanze Nov 06 '13 at 10:23

3 Answers3

6

The -> operator is (mostly) an abbreviation for dereference (*) and selection (.). In other words, a->b is the same as (*(a)).b. (§5.2.5/2; See notes below).

The . syntax is class member access, as defined by §5.2.5 [expr.ref]; the identifier on the right-hand side of the . can be a static or non-static data member, function, or member enumerator (paragraph 4 of the cited section). It cannot be a nested type. In this sense, member enumerators are syntactically similar to static const data members.

Notes:

  1. As §13.5.6 clarifies, a->b is is subject to operator overloading. If a is not a pointer type, then -> may be overloaded, in which case the expression is interpreted as (a.operator->())->b. Eventually, the sequence of overloaded -> calls must result in a pointer type, at which point the interpretation of §5.2.5/2 is applied.

  2. An important difference between Class::member and value.member is that in the second case, value will be evaluated even if that is unnecessary to resolve the value of member.

rici
  • 234,347
  • 28
  • 237
  • 341
  • Thank you for pointing to the standard. I wish that we had a copy of the standard that I could view. – Gandalf458 Nov 05 '13 at 18:50
  • @Gandalf458: public drafts are available from the C++ standards committee's page: http://www.open-std.org/jtc1/sc22/wg21/ These drafts are what most of us use :) – rici Nov 05 '13 at 19:13
3

From C++ ISO/IEC 2011

An enumerator declared in class scope can be referred to using the class member access operators (::, . (dot) and -> (arrow)),

Jimbo
  • 4,352
  • 3
  • 27
  • 44
  • The point wasn't to redesign the class. It was to say why that pointer has access to the enum. – Gandalf458 Nov 05 '13 at 18:05
  • May be you should mention that `.` notation should also work for instances of `MyClass`. – πάντα ῥεῖ Nov 05 '13 at 18:10
  • I appreciate your responses, but I never got an error with this pseudocode. Look back at my post where I added the public to clarify that that was not the question. I just wanted to know why a pointer to a class can see the enum (assume every nook and cranny is public) as opposed to needing the scope :: operator. – Gandalf458 Nov 05 '13 at 18:13
  • Your original didn't have `public` in it as far as I could see... it would have been better if you posted that when I read it :) Did you mean to have a single `=`'s in your `if` statement? – Jimbo Nov 05 '13 at 18:19
  • I wish I could select everyone's answer. They all work together to do a great job at explaining the issue. @Jimbo – Gandalf458 Nov 05 '13 at 21:19
2

The enum values are treated much as if they were static members of the class, and can be accessed in two ways: via the class name followed by the scope resolution operator (MyClass::MY_VALUE0), or like any other member (instance.MY_VALUE0 or pointer->MY_VALUE0).

Note that in the latter case, the operand on the left is still evaluated, even though the results of the evaluation is not used. In other words, if I write f()->MY_VALUE0 (where f() returns a MyClass*), the function will be called, despite the fact that its return value is not used.

James Kanze
  • 150,581
  • 18
  • 184
  • 329