Clang is wrong: foo
in the typedef declaration in A
does not refer to the namespace-scope typedef-name foo
W.r.t. the standard rules, the enclosing namespace/scope alias declaration
using foo = int;
is a red herring; within the declarative scope of class A
it will be shadowed by names declared in A
#include <type_traits>
using foo = int;
struct A {
using foo = char;
foo x;
};
static_assert(std::is_same_v<foo, int>,"");
static_assert(std::is_same_v<A::foo, char>,"");
static_assert(std::is_same_v<decltype(A::x), char>,"");
The key here being that typedef A (foo)();
declares the name foo
within the declarative region of A
, as per [dcl.spec]/3 [emphasis mine]:
If a type-name is encountered while parsing a decl-specifier-seq, it is interpreted as part of the decl-specifier-seq if and only if there is no previous defining-type-specifier other than a cv-qualifier in the decl-specifier-seq.
Specifically, this means that in the typedef declaration
typedef A (foo)();
even if there is an existing typedef-name foo
, that foo
is not considered in the typedef declaration, namely it is not considered as a type-name part of the decl-specifier-seq of typedef A (foo)()
, as A
has already been encountered previous to it, and A
is a valid defining-type-specifier. Thus, the original example:
using foo = int;
struct A {
typedef A (foo)();
};
can be reduced to:
// (i)
struct A {
typedef A (foo)(); // #1
};
which declares the typedef name foo
in A
(A::foo
), where the paranthese around the name are redundant, and the typedef declaration at #1 can likewise be written as
// (ii)
struct A {
typedef A foo(); // #1
};
and can likewise be introduced using an alias declaration ([dcl.typedef]/2):
// (iii)
struct A {
using foo = A();
};
(i)
, (ii)
and (iii)
are accepted by both GCC and Clang.
Finally, we may note that Clang accepts the following program:
using foo = int;
struct A {
typedef A foo();
using bar = A();
};
static_assert(std::is_same_v<A::foo, A::bar>,"");
and that the root issue of the example of the OP is arguably a Clang bug, where Clang fails to adhere to [dcl.spec]/3 and interprets the outer-scope typedef-name foo
as part of the decl-specifier-seq of the inner-scope typedef declaration, only for the case where the latter has wrapped the shadowed name foo
in parantheses.