2

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)

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
tjm
  • 7,500
  • 2
  • 32
  • 53
  • Sorry why are you doing this? – Brian R. Bondy Jul 05 '10 at 01:19
  • 4
    Why on _earth_ would you "hope" for behavior that obscure (especially with regard to 5+ enum values) ? _What are you really trying to do_? – Stephen Jul 05 '10 at 01:19
  • 1
    The number of elements in the enumeration should not affect the behaviour. To me, such varying behaviour smacks of 'bug' (in GCC). Which version of GCC are you using? (But I'll also agree with the other commentators: what you are trying to do seems pretty weird!) – Jonathan Leffler Jul 05 '10 at 01:19
  • @Brian and Stephen, I was writing some code for iterating over enums and hit upon it by accident really. @Johnathan, using gcc 4.4.3. – tjm Jul 05 '10 at 01:23
  • You are not supposed to do this or that? Is this a communist forum? Let the guy explore the envelope of the language. If any language allows anything to be done that is not supposed to be done - that is the fault and folly (and limits of capability) of the designers of the language. – Blessed Geek Jul 05 '10 at 01:29
  • whoever mentions nazis first in any argument loses – slf Jul 05 '10 at 01:31
  • @h2g2java : Nobody said anybody is "supposed to do" anything. We're merely advocating that there's probably a better way to get the behavior tjm wants. @slf : Who mentioned the nazis? (You are aware the nazis weren't communist, right?) – Stephen Jul 05 '10 at 01:38
  • Communist nazis only exist on the Simpsons. – zildjohn01 Jul 05 '10 at 01:43
  • Could be a possible bug in g++. g++ version 4.4.4(on Debian testing) reports errors at line #3 and line #7. Out of curiosity, why would you hide the name of an enum while leaving its elements public? – vpit3833 Jul 05 '10 at 01:59
  • Sorry my previous comment didn't really (or at all) answer the Q of what I was trying to do. Basically i've got an enum class (not c++0x style, a class representing an enum) that extends a struct containing an enum. It needs to inherit the enum constants and also have access to the name so that it can have a constructor that takes the constants. the name should not be accessible however to anyone else or they could cast any old int to the type and use it to construct an invalid instance. – tjm Jul 05 '10 at 17:10

4 Answers4

6

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?

Community
  • 1
  • 1
zildjohn01
  • 11,339
  • 6
  • 52
  • 58
  • 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 Jul 05 '10 at 01:45
  • 3
    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 Jul 05 '10 at 02:03
  • 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 Jul 05 '10 at 17:53
2

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.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
0

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
  • 3,631
  • 1
  • 23
  • 27
0

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
  • 730,956
  • 141
  • 904
  • 1,278