tags:

views:

98

answers:

4

Sorry for the not very descriptive question title. I'm not very sure how to describe this, hopefully I can make it better later if someone can explain this to me.

I was about to come on here and ask why the following example was working. It was exhibiting the behaviour I was hoping for but I wasn't sure why or whether it was standard or whether I just got lucky with the compiler. Anyway, in sorting out a minimal working example to post here I found that it wasn't doing what I thought at all. So here it is ...

   struct Foo {
       enum BAR { A, B, C, D, E };
       private: typedef BAR BAR;
   };

   int main(int argc, char* argv[]) {
       int x = (Foo::BAR)42;
       int y = Foo::D;
  }

What seems to be happening, and what I was quite pleased about, is that, Foo takes on the enum constants after which BAR is made private. So I get no error on int y = but I get a Foo::BAR is private error at int x=. However this seems to only work with 5 or more constants in the enum, remove that E and it all compiles fine, i.e. BAR remains public.

What's at work here? Thanks.

(PS. Compiler is GCC 4.4.3)

A: 

I would hate to say something like "Microsoft got it right" but I can't find anything in the standard that would advocate this kind of behavior. As far as I can tell, the typename Foo::BAR should always remain publicly accessible.

There is no deviation in this behavior from MSVC++. It always allows your code sample to compile without error. I tried everything up to 26 entries in BAR.

I would dare say this is a bug in gcc.

However I have to agree with what has already been said - I don't think this code deserves to be compiled.

Shirik
A: 

Running 'g++ -c file.cpp' on MacOS X 10.6.4 with G++ 4.2.1, the code compiles without a whimper with both 4 and 5 elements in the enumeration. Adding '-Wall -pedantic' only triggers complaints about unused variables x and y.

As noted in a comment (and the question title), the number of elements in the enumeration should not affect the behaviour. To me, such varying behaviour smacks of 'bug' (in GCC).

Which is the correct behaviour is more complex; I'm loath to take a strong stance on that. On average, I favour 'Foo::BAR' was first declared public and the later private typedef should be ignored or should be an error. However, that is very far from being a definitive view on what the behaviour should be - I am very uncertain.

Jonathan Leffler
+5  A: 

I can verify your results about four vs. five enum elements... this looks like an obscure GCC bug.

As for "what's at work here", it's due to a technicality between different symbol namespaces in C++ (inherited from C). See this answer for more details.

If you don't want a symbol named BAR to be exposed, simply omit the tag name:

struct Foo {
    enum { A, B, C, D, E };
};

If you declare a named public enum, then no matter what you do, even if you hide the name using a typedef, outsiders can access the enum using an elaborated type specifier. Trying to hide a symbol name in this way is fruitless:

 struct Foo {
     enum BAR { A, B, C, D, E };
     private: typedef BAR BAR;
 };

 int main(int argc, char* argv[]) {
     int x = (enum Foo::BAR)42;  // works great!
     int y = Foo::D;
}

Is there any particular reason you want the constants to be public while the enum name remains private?

zildjohn01
I was writing an iterable enum class, it inherited from an enum wrapped in a struct. It was all ok, the top class was typesafe, but if someone wanted to (or through error) they could cast an invalid int into the base struct type and use that to get an invalid enum state. I wasn't looking to solve it via this method but at some point hit on this and it seemed to work. so i thought i should find out whether it was actually valid and I should do it. It seems the consensus is, its not and no!
tjm
I would avoid trying to protect your users from themselves, especially in C++. Great idea in theory, not so much in practice. If they're casting all willy-nilly around your types, they deserve that invalid enum state. And besides, if they're determined, nothing will stop a `reinterpret_cast`.
zildjohn01
Thankyou, I think thats a good piece of advice. The truth is, the struct is generated with a unique name via a macro so it is unlikey anyone would do it, I was just envisaging someone reading the typename in an error message and just going oh well I can get around that by casting. But I suppose if they do, then thats their problem! I also just realised that even if the example had worked someone determined to break things could quite easily just create their own type that inherits from the struct and cast from that. So it wouldn't have locked things up entirely anyway.
tjm
+1  A: 

The typedef name should not conflict with the enum type name. Instead, the typedef name should hide the previously declared enum type name. Since the typedef name is private, it should be inaccessible from outside

Foo::BAR i; // ERROR, `Foo::BAR` is private

Still, you can refer to the hidden public enum type name by using the elaborate type specifier

enum Foo::BAR i; // OK

The enum constants themselves are, of course, public and should remain accessible in your example.

The behavior should not depend on the number of constants in the enum. If you observe the dependence you describe, it must be a bug in the compiler.

AndreyT