1

name lookup

C++ has a principle that identifiers must be declared before they are used. When each identifier is used, its corresponding declaration must be found. This process is the name lookup.

The rules for name lookup vary in different structures of the program. It is briefly described below.

Unqualified name lookup

Simply put, it is a lookup of a single identifier.

Use of identifiers in namespaces

An identifier used in a namespace needs to be declared in the current or containing namespace before it can be used.

in a function defined in the namespace

This includes any symbols that appear after the function name, including types, default parameters, etc. appearing in the parameter list.

Its declaration needs to appear before it is used. Find the current block in turn, the block containing the current block, the namespace to which the function belongs, and the namespace's parent namespace.

namespace A {
    namespace N {
        void f();
    }
}
void A::N::f() {
    i = 5;
    // i 的声明会在以下地方被查找:
    // 1) A::N::f 中,i 的只用之前
    // 2) namespace N
    // 3) namespace A
    // 4) 全局名字空间,A::N::f 的定义之前
}

the name in the class definition

A name in a class definition is looked up differently depending on whether it is in a complete-class context.

Complete-class context refers to: function body, function default parameters, noexcept-specifier, member initialization

Outside the complete-class context, before using the declaration, the search scope includes: the current class, the members of the base class, the class that contains the definition of the current class (if any) or its base class members, the function that contains the current class (if any) , contains the namespace of the current class.

namespace M {
    class B { };
}
namespace N {
    class Y : public M::B {
        class X {
            int a[i];
        };
    };
}
// 会在以下位置查找 i 的声明:
// 1) class N::Y::X 中,i 使用之前
// 2) class N::Y 中, N::Y::X 定义之前
// 3) N::Y’s 的基类 M::B
// 4) 名字空间 N, N::Y 定义之前
// 5) 全局名字空间, N 定义之前

In a complete-class context, when looking for class members, the member declaration is not required before the identifier is used, and the search scope includes: the current block or the containing block (if any), the current class member or the The members of the class defined by the class (if any) or its base class members, the functions that contain the current class (if any), the namespace that contains the current class.

class B { };
namespace M {
    namespace N {
        class X : public B {
            void f();
        };
    }
}
void M::N::X::f() {
    i = 16;
}
// 会在以下位置查找 i 的声明:
// 1) M::N::X::f, i 的使用之前
// 2) class M::N::X
// 3) M::N::X 的基类 B
// 4) namespace M::N
// 5) namespace M
// 6) global scope, M::N::X::f 定义之前

friend

If a friend function is defined in a class, its defined name lookup rules are the same as those of class members.

If the friend function is a class member, the identifier will be looked up first in the class in which the function is located, unless it is an identifier for template-argument in declarator-id .

struct A {
    typedef int AT;
    void f1(AT);
    void f2(float);
    template <class T> void f3();
};
struct B {
    typedef char AT;
    typedef float BT;
    friend void A::f1(AT); // parameter type is A::AT
    friend void A::f2(BT); // parameter type is B::BT
    friend void A::f3<AT>(); // template argument is B::AT
};

Default parameters, mem-initializer

In the default parameters, expression of mem-initializer , function parameters are visible and override the outer declaration of the same name.

enumerate

In an enum definition, the defined enumerator is visible in subsequent definitions

static member

In the definition of a static member of a class, the name lookup rule is a member function of the same class

data members of the namespace

If the definition of a data member of a namespace occurs outside the namespace, the identifier lookup in its definition follows the same rules as when it was defined in its namespace.

namespace N {
    int i = 4;
    extern int j;
}
int i = 2;
int N::j = i; // N::j == 4

Argument-dependet name lookup

This rule is used for function calls.

typedef int f;
namespace N {
    struct A {
        friend void f(A &);
        operator int();
        void g(A a) {
            int i = f(a); // f is the typedef, not the friend function: equivalent to int(a)
        }
    };
}

This lookup method is only used when the function is unqualified-id .

namespace N {
    struct S { };
    void f(S);
}
void g() {
    N::S s;
    f(s); // OK: calls N::f
    (f)(s); // error: N::f not considered; parentheses prevent argument-dependent lookup
}

When the usual unqualified name lookup can be found

  • a class member
  • A block-level function declaration (remove using-declaration )
  • When not a function or function template declaration

Argument-dependent name lookup does not take effect. Otherwise, the results of the lookup will contain the union of the ordinary lookup results and the Argument-dependent name lookup results.

Argument-dependent name lookup actually looks in the same namespace as the argument type, and:

  • Ignore using-directive
  • Declarations that are not functions and function templates are ignored
  • Metafunctions defined in classes can be found

Qualified name lookup

In the form of A::B::C , searches for B and C are all qualified name lookups.

Look in the global namespace starting with :: .

If the part before :: is an enumeration, it should be the enumerator in that enumeration.

... type-name :: ~ type-name in (two type-name not necessarily the same), the second type-name the first type-name look in the same range .

When before :: is not an enumeration, it may be a class, or a namespace.

class member

When it is a class before :: , it is searched in class members and base classes. (Note that it is a base class, not a member of the base class.) But there are a few exceptions:

  • lookup for destructors (see above)
  • conversion-function-id in conversion-type-id lookup like member access
  • template-id in template-argument in the context of the entire postfix-expression
  • The name in using-declaraion , you can find the currently hidden class or enumeration

namespace member

A lookup for members of the namespace will start with the members of the namespace itself. Members introduced via using-directive will only be looked up if this lookup yields no results.

Elaborated type specifiers

Such as class A , struct B::C and so on.

For unqualifed-id, follow the unqualified name lookup rules, but ignore all non-type declarations.

For class , struct , union a new class may be declared if not found.

If it is a qualified-id, use the Qualified name lookup rule, ignoring all non-type names. At this point, the new type is not declared even if it cannot be found.

Class member access

a.b, p->c

If id-expression is an unqualified-id, then do a lookup in the object class.

In the form a.B::b , B will be looked up in the class of a first. If not found, it will look in the context of the entire postfix-expression .

Look up the entire postfix-expression context for template-arguments in simple-template-id .

In conversion-function-id , its conversion-type-id in object class. If not found, look in the entire postfix-expression . Names other than types (or type templates) are ignored during all lookups.


fefe
18k 声望7.1k 粉丝