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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With