Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

May I declare a member type alias to a type in a surrounding scope, using the same name?

I want a struct to contain a type alias to another type for metaprogramming purposes:

struct Foo {};

struct WithNestedTypeAlias {
    using Foo = Foo;
};

Then I can do stuff like WithNestedTypeAlias::Foo in a template etc.

As I understand, this type alias is valid because it does not change the meaning of the Foo type. Clang compiles this happily.

However, GCC complains:

test-shadow-alias.cpp:4:20: error: declaration of ‘using Foo = struct Foo’ [-fpermissive]
     using Foo = Foo;
                    ^
test-shadow-alias.cpp:1:8: error: changes meaning of ‘Foo’ from ‘struct Foo’ [-fpermissive]
 struct Foo {};
        ^

Now I'm confused because I'm explicitly not changing the meaning of Foo from struct Foo.

What is the correct behaviour for C++14? I know I can work around this by renaming the struct Foo, but I'd like to understand whether GCC's error is correct here.

Notes:

  • Tested with clang++ 3.8 and gcc 5.4, but Godbolt suggests this hasn't changed in more recent GCC versions.

  • I looked at Interaction between decltype and class member name shadowing an external name, where the name of a variable may refer to either a variable in the outer scope or to a class member. In contrast, my question here is about a type alias. There is no ambiguity since Foo always refers to ::Foo within the class scope. I don't see how the answer there applies to my problem.

  • This is probably due to a misunderstanding of what type aliases actually are.

like image 731
amon Avatar asked Jan 06 '17 23:01

amon


1 Answers

The rule GCC is enforcing is in [basic.scope.class]:

2) 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.

The standard says violating this doesn't require a diagnostic, so it's possible that both GCC and Clang are conforming, because (if GCC is right) the code is not valid, but the compiler is not required to diagnose it.

The purpose of this rule is so that names used in a class always mean the same thing, and re-ordering members doesn't alter how they are interpreted e.g.

struct N { };

struct S {
  int array[sizeof(N)];

  struct N { char buf[100]; };
};

In this example the name N changes meaning, and reordering the members would change the size of S::array. When S::array is defined N refers to the type ::N but in the completed scope of S it refers to S::N instead. This violates the rule quoted above.

In your example the name Foo changes in a far less dangerous way, because it still refers to the same type, however strictly speaking it does change from referring to the declaration of ::Foo to the declaration of S::Foo. The rule is phrased in terms of referring to declarations, so I think GCC is right.

like image 56
Jonathan Wakely Avatar answered Oct 18 '22 18:10

Jonathan Wakely