模板库中的名称查找:我们为什么要添加这个->

Name lookup in template base: why do we add this->

考虑以下几点:

struct Base {
    void foo();
};

template <class T>
struct Derived : Base {
    void bar() {
        this->foo();
    }
};

通常,我们解释说 this-> 使 foo() 成为从属名称,因此它的查找被推迟到第二阶段,即模板实例化点。

但是第二阶段查找只调用 ADL,不考虑成员函数,是吗?

任何指向解释上述代码编译原因的标准段落的指针,我将不胜感激。

But the second phase lookup invokes only ADL, which does not consider member functions, does it?

没有。尽管 ADL 添加到查找集中,但它并没有包含所有查找集。此外,当所述后缀表达式是 unqualified-id.

时,您在这里解释的想法适用于 postfix-expression(args) 中的后缀表达式

[temp.dep]

1 ... In an expression of the form:

postfix-expression ( expression-listopt )

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

  • [... specific conditions ...]

If an operand of an operator is a type-dependent expression, the operator also denotes a dependent name. Such names are unbound and are looked up at the point of the template instantiation ([temp.point]) in both the context of the template definition and the context of the point of instantiation.

因此,如果您有 foo() 而不是,查找将不会考虑成员,而是只会尝试自由函数,无论是在定义点还是实例化点(ADL 可以添加到查找集,假设我们有一个依赖表达式)。

但是对于 this->foo(我故意省略了调用,以讨论后缀表达式),我们有 class 成员访问权限。此处适用其他段落:

[temp.dep.type]

5 A name is a member of the current instantiation if it is

  • [...]
  • An id-expression denoting the member in a class member access expression for which the type of the object expression is the current instantiation, and the id-expression, when looked up, refers to at least one member of a class that is the current instantiation or a non-dependent base class thereof. [ Note: If no such member is found, and the current instantiation has any dependent base classes, then the id-expression is a member of an unknown specialization; see below.  — end note ]

6 A name is a member of an unknown specialization if it is

  • [...]
  • An id-expression denoting the member in a class member access expression in which either
    • the type of the object expression is the current instantiation, the current instantiation has at least one dependent base class, and name lookup of the id-expression does not find a member of a class that is the current instantiation or a non-dependent base class thereof; or

7 Similarly, if the id-expression in a class member access expression for which the type of the object expression is the current instantiation does not refer to a member of the current instantiation or a member of an unknown specialization, the program is ill-formed even if the template containing the member access expression is not instantiated; no diagnostic required.

这些项目符号告诉我们遇到 this->foo 时要执行的查找。它只查找成员。在我们的例子中,我们在当前实例化中有一个非依赖基 class,因此可以明确地找到成员。

找到成员函数后,后缀表达式this->foo表示可调用,函数调用就是这样解析的。

“两阶段查找”一词被广泛误解,甚至在专家中也是如此。 C++17 都说

Such names are unbound and are looked up at the point of the template instantiation (17.6.4.1) in both the context of the template definition and the context of the point of instantiation.

(在 [temp.dep]/1 中)和

In resolving dependent names, names from the following sources are considered:
— Declarations that are visible at the point of definition of the template.
— Declarations from namespaces associated with the types of the function arguments both from the instantiation context (17.6.4.1) and from the definition context.

(in [temp.dep.res]/1),每一个似乎都暗示这是两个阶段。然而,更好的解释这个(非官方)短语的方法是 lookup is done

  1. 来自模板定义:对于非依赖名称
  2. 实例化期间:来自定义,但也仅通过 ADL,来自实例化上下文

前者可以解析模板定义时完成,但对于无法实例化的模板不需要诊断([temp.res]/8).请注意,后者包括的不仅仅是 ADL。

相关措辞近期已明确;它现在只是说

[Note: For the part of the lookup using associated namespaces ([basic.lookup.argdep]), function declarations found in the template instantiation context are found by this lookup, as described in [basic.lookup.argdep]. — end note]

作为第二阶段差异的提醒。尽管如此,它混淆地使用 dependent name 仅指代 unqualified dependent names/operators ([temp.dep]/2)。像 Derived::foo 这样的名字(来自 this->)是 clearly dependent;作为限定名称,它们不受 ADL 约束,但在实例化期间仍会查找它们——这允许找到来自 dependent 基的结果。 (你的例子有一个非依赖的基础,但那也是可用的。)