12

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:

Community
  • 1
  • 1
amon
  • 57,091
  • 2
  • 89
  • 149

1 Answers1

10

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.

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
Jonathan Wakely
  • 166,810
  • 27
  • 341
  • 521
  • Hmm, that rule does make sense. But this seems to imply that I shouldn't use the `using Foo = ::Foo` trick mentioned by François Andrieux in a comment above? While that currently happens to work, it is still a new declaration of `Foo` and thus in violation of your quoted rule, right? So that would leave me with the only solution `struct AnotherFoo{}; struct Nested { using Foo = AnotherFoo; }; using Foo = AnotherFoo;` for doing the nested `Foo` declaration portably. – amon Jan 07 '17 at 00:09
  • 3
    @amon: The issue in your original code is that the `Foo` on the right hand side changes meaning because it is unqualified. Before the `using` declaration, it would have meant the global Foo, later it would have meant the alias. If it is qualified, then that is no longer an issue. – Vaughn Cato Jan 07 '17 at 05:50
  • 2
    @amon Also note [CWG 42](http://wg21.link/cwg42) that will answer your question about `Foo`, `::Foo` and everything. – bogdan Jan 07 '17 at 12:01
  • @amon The rule is not that `Foo` mustn't change its meaning, but that it mustn't be _used_ before having its meaning changed. So `using Foo = ::Foo` is OK because the unqualified `Foo` is not used before having its meaning changed. Similarly, the example in the answer would be OK if we simply replace `sizeof(N)` with `1`. – Oktalist Jan 07 '17 at 16:40
  • @Oktalist OK, I understand that now. Thank you for your assistance :) – amon Jan 07 '17 at 16:58