Is it legal to redefine a type name?

147 views Asked by At

Chapter 7.4.1 of the book C++ Primer says the following:

Ordinarily, an inner scope can redefine a name from an outer scope even if that name has already been used in the inner scope. However, in a class, if a member uses a name from an outer scope and that name is a type, then the class may not subsequently redefine that name

The word subsequently makes me think that it is only an error to redefine a type name after it has been used in the class. However, two paragraphs later it says:

Although it is an error to redefine a type name, compilers are not required to diagnose this error.

Does this mean that it is an error to redefine a type name even before it has been used in a class?

2

There are 2 answers

0
Brian Bi On

The OP writes in the comment section:

... My question is wether [sic] it is legal to redefine a type name before it has been used in a class.

The answer is yes. It is legal to define a type name in a class that shadows a type name in an enclosing scope. What is not allowed is the following:

  • A name, call it T (though it need not be a type), is used inside a class, and name lookup finds some declaration D, and
  • The class declares a member named T, and
  • After the class has been completely defined, if the compiler goes back and parses the previous use of the name T, it now finds the new member declaration instead of D.

Let's go over a few examples to clarify the issue.

using T = X;
class C {
    using T = Y;
    T x;
};

The above example is legal because when the compiler sees T x;, it has already seen using T = Y;, so T can only resolve to Y. There are no subsequent declarations inside the class that can change the meaning later.

using T = X;
class C {
    T x;
    using T = Y;
};

The above example is illegal. The compiler at first looks up T and finds the enclosing definition of T as X, since C::T hasn't been declared yet. But after C::T has been declared, it implies that if T x; were to be reinterpreted, then T would now refer to C::T instead, since the class scope takes precedence over the enclosing scope.

using T = ::X;
class C {
    T x;
    using T = ::X;
};

The above example is illegal. The fact that both declarations of T make it refer to the same type is not relevant. The violation occurs because T x; would find a different declaration of T after the class is completely defined.

using T = X;
class C {
    ::T x;
    using T = Y;
};

The above example is legal. The declaration using T = Y; does not change the meaning of ::T, because ::T will always refer to the global declaration even though C::T is there.

using T = X;
class C {
    void foo() { T x; }
    using T = Y;
};

The above example is legal. The body of foo is a complete-class context, which implies that name lookup for T is not done until after the entire class has been seen. Put another way, the declaration using T = Y; in a sense always "precedes" the body of foo so this is similar to the first example.

2
Vlad from Moscow On

It seems that the quote refers to the following case

typedef int T;

struct A
{
    T a[10];
    typedef double T;
};

That is the code is ill-formed due to the redeclaration of the name T within the class definition that was already used in the declaration of the data member a.

If you will just write

typedef int T;

struct A
{
    int a[10];
    typedef double T;
};

then this code is correct. The name T is not used as a type specifier in declarations of members of the class A.