typedef changes meaning

When I compile the following snippet with g++

template<class T>
class A
{};

template<class T>
class B
{
    public:
        typedef A<T> A;
};

the compiler tells me

error: declaration of ‘typedef class A<T> B<T>::A’
error: changes meaning of ‘A’ from ‘class A<T>’

On the other hand, if I change the typedef to

typedef ::A<T> A;

everything compiles fine with g++. Clang++ 3.1 doesn't care either way.

Why is this happening? And is the second behavior standard?


g++ is correct and conforming to the standard. From [3.3.7/1]:

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.

Before the typedef, A referred to the ::A, however by using the typedef, you now make A refer to the typedef which is prohibited. However, since no diagnostic is required, clang is also standard conforming.

jogojapan's comment explains the reason for this rule. Take the following change to your code:

template<class T>
class A
{};

template<class T>
class B
{
    public:
        A a; // <-- What "A" is this referring to?
        typedef     A<T>            A;
};

Because of how class scope works, A a; becomes ambiguous.


I will add to Jesse's answer about the seemingly peculiar behavior of GCC in compiling:

typedef A<T> A;

vs

typedef ::A<T> A;

This also applies to using statements as well of the form:

using A =   A<T>;
using A = ::A<T>;

What seems to be happening within GCC, is that during the compilation of the typedef/using statement declaring B::A, that the symbol B::A becomes a valid candidate within the using statement itself. I.e. when saying using A = A<T>; or typedef A<T> A; GCC considers both ::A and B::A valid candidates for A<T>.

This seems odd behavior because as your question implies, you don't expect the new alias A to become a valid candidate within the typedef itself, but as Jesse's answer also says, anything declared within a class becomes visible to everything else inside the class - and in this case apparently even the declaration itself. This type of behavior may be implemented this way to permit recursive type definitions.

The solution as you found is to specify for GCC precisely which A you're referring to within the typedef and then it no longer complains.