2

The lookup for T should fail

class A
{
    typedef int T;
    void f(void)
    {
        class B {};
        class C
        {
            class D : public B
            {
                void g(void)
                {
                    T a;
                }
            };
        };
    }
};

because 3.4.1 paragraph 8 from N4567 says that

For the members of a class X, a name used in a member function body, in a default argument, in an exception-specification, in the brace-or-equal-initializer of a non-static data member (9.2), or in the definition of a class member outside of the definition of X, following the member’s declarator-id, shall be declared in one of the following ways:

  • before its use in the block in which it is used or in an enclosing block (6.3), or
  • shall be a member of class X or be a member of a base class of X (10.2), or
  • if X is a nested class of class Y (9.7), shall be a member of Y, or shall be a member of a base class of Y (this lookup applies in turn to Y’s enclosing classes, starting with the innermost enclosing class), or
  • if X is a local class (9.8) or is a nested class of a local class, before the definition of class X in a block enclosing the definition of class X, or
  • if X is a member of namespace N, or is a nested class of a class that is a member of N, or is a local class or a nested class within a local class of a function that is a member of N, before the use of the name, in namespace N or in one of N’s enclosing namespaces.

To be more specific, the lookup for T is done as the following:

  1. before T's use in g
  2. shall be a member of class D or be a member of class B
  3. shall be a member of class C
  4. before the definition of class D in f
  5. ???

The problem is that D is not

  • a member of a namespace,
  • a nested class of a class that is a member of a namespace,
  • a local class,
  • a nested class within a local class of a function that is a member of a namespace,

but rather a nested class within a local class of a function that is a member of a class.

1 Answers1

0

This is just a problem with the way the name lookup rules were specified in the standard until very recently.

We all know what the order should be: the block scope of g, then the scopes of enclosing entities, starting with the innermost enclosing entity. That is, D, then C, then f, then A, then the global namespace. But if any of these entities is a class, then its base classes are searched after it and before the next enclosing entity. But the wording did not do a good job of describing it. As pointed out in the comments, this issue was CWG 191, which was only resolved recently by "Declarations and were to find them". The new and improved wording will be in C++23. Let's take a look at what the draft says...

Clearly the name lookup for T in this case is unqualified name lookup, so let's start there. Unqualified name lookup performs an unqualified search ([basic.lookup.unqual]/3) in its immediate scope (which is defined elsewhere; suffice it to say that it's the block scope of g).

An unqualified search of the block scope of g performs a search in that scope. It would also search other scopes if any using-directives were active, which is not the case in your example. Finally, if the search finds nothing, then it searches the parent scope of whatever scope was being searched ([basic.lookup.unqual]/2). Thus, the block scope of g will be searched first, then D, then C, then f, then A, then the global namespace.

A search is defined in [class.member.lookup]. When the scope being searched is not a class scope, it's just a single search. When it is a class scope, a complicated algorithm is used to perform a single search in that class first; then, if nothing is found, it goes to the base classes.

So g is single-searched first, then D, then B, then C, then f, then A, then the global namespace.

A single search in a given scope is defined in [basic.lookup.general]/3. It finds declarations that bind the name being searched for to that scope, and that precede the point where the name lookup occurs. In the example, the declaration typedef int T; binds the name T in the class scope of A.

Brian Bi
  • 111,498
  • 10
  • 176
  • 312