Search code examples
c++typedefinner-classes

Why can't I make a external type appear like an inner type in c++?


I want to maintain an existing API that heavily uses an inner enum but I need to separate the enum out into its own type:

This is the current API:

class Outer {
public:
    enum Inner {
        FIELD1
    };
};

In other words, users currently expect to set Inners by using Outer::Inner. I would like to preserve the name if possible when I make the new outside type:

enum Inner {
    FIELD1
};

class Outer {
public:
    typedef Inner Inner;
}

However - it gives me a compiler error:

./Playground/file0.cpp:23:19: error: declaration of 'typedef enum Inner Outer::Inner' changes meaning of 'Inner' [-fpermissive]
   23 |     typedef Inner Inner;
      |                   ^~~~~
./Playground/file0.cpp:17:6: note: 'Inner' declared here as 'enum Inner'
   17 | enum Inner {
      |      ^~~~~

If I just change the name of the enum it all works fine.

#include <cassert>

enum Enum {
    FIELD1
};

class Outer {
public:
    typedef Enum Inner;
};

int main() {

    Enum field = Enum::FIELD1;
    Outer::Inner field_alt = Outer::Inner::FIELD1;
    assert(field == field_alt);
    return 0;
}

Solution

  • This is not allowed because of the rule [basic.scope.class]/3:

    A name N used in a class S shall refer to the same declaration in its context and when re-evaluated in the completed scope of S. No diagnostic is required for a violation of this rule.

    When you do typedef Inner Inner;, you're violating this rule. The first time Inner appears on that line, it is a use of the name, and is found to refer to the declaration ::Inner. But once the class is completely defined, Inner now refers to the typedef declaration. So the program is ill-formed NDR (GCC is nice enough to give you a diagnostic).

    You need to change it to:

    typedef ::Inner Inner;
    

    Now, the unqualified name Inner is not being used anymore; instead you are using the qualified name ::Inner, and its meaning stays the same after the typedef, so there should be no issue.