Lookup of dependent names in C++ template instantiation

后端 未结 3 1921
执笔经年
执笔经年 2021-02-07 05:11

When I try to compile this code

// void foobar(int); 

template 
struct Foo {
  void bar(T t) { foobar(t); };
};

void foobar(int);

template clas         


        
3条回答
  •  别跟我提以往
    2021-02-07 05:37

    tl;dr Foo doesn't invoke any ADL, but Foo would (where X is a class type).


    First of all, in this code foobar is a dependent name because of (C++14/N3936) [temp.dep]/1

    In an expression of the form:

    postfix-expression ( expression-list opt )
    

    where the postfix-expression is an unqualified-id, the unqualified-id denotes a dependent name if [...]

    • any of the expressions in the expression-list is a type-dependent expression (14.6.2.2), or

    and t is a dependent name because it is part of a declaration T t where T is a template parameter and thus a dependent type.

    Moving onto dependent name resolution, there is [temp.dep.res]/1 which introduces the fact that names can be both looked up in the definition context, and the instantiation context, and defines where the instantiation context is. I have omitted that for brevity, but in this example template class Foo; is the point of instantiation.

    The next bit is [temp.dep.candidate]/1:

    For a function call where the postfix-expression is a dependent name, the candidate functions are found using the usual lookup rules (3.4.1, 3.4.2) except that:

    • For the part of the lookup using unqualified name lookup (3.4.1), only function declarations from the template definition context are found.
    • For the part of the lookup using associated namespaces (3.4.2), only function declarations found in either the template definition context or the template instantiation context are found.

    Those last two parts are the "two phases" of two-phase lookup. (Note - this section changed in wording from C++11 to C++14, but the effect in the same).

    In the first phase, 3.4.1, no names are found for foobar.


    So we move onto the second phase. The actual places that names are looked up as described in 3.4.2. The text is long but the here are two of the relevant rules:

    • If T is a fundamental type, its associated sets of namespaces and classes are both empty.

    • If T is a class type (including unions), its associated classes are: the class itself; the class of which it is a member, if any; and its direct and indirect base classes. Its associated namespaces are the innermost enclosing namespaces of its associated classes. [...]

    So when you instantiate Foo, then the second phase of lookup does not introduce any additional namespaces to search.

    However, if you change your example to have struct X {}; and then change int to X everywhere, then the code does compile. This is because of the latter bullet point: ADL for an argument of class type does search the enclosing namespace of that class (which is the global namespace now), however ADL for an argument of built-in type does not search the global namespace.

提交回复
热议问题