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

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

本文关键字:添加 我们 gt 为什么 查找      更新时间:2023-10-16

考虑以下内容:

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

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

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

如果有任何指向标准段落的指针,我将不胜感激,它解释了为什么要编译上面的代码。

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

没有。然而,ADL添加到查找集,但它并不包括所有查找集。此外,您在这里转述的想法适用于postfix-expression(args)中的后缀表达式,当所述后缀表达式是不合格id时。

[temp.dep]

1。。。在形式的表达式中:

后缀表达式(表达式列表opt)

其中后缀表达式是不合格的id如果,则表示从属名称

  • […具体条件…]

如果运算符的操作数是依赖于类型的表达式,则运算符还表示从属名称。此类名称未绑定在中查找模板实例化的点([temp.point])模板定义的上下文和实例化点。

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

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

[温度依赖类型]

5如果名称是,则它是当前实例化的成员

  • […]
  • id表达式表示类成员访问表达式中的成员,对象表达式的类型是当前实例化,并且当查找时,id表达式指的是作为当前实例化的类或其非依赖基类的至少一个成员。[注意:如果没有找到这样的成员,并且当前实例化有任何依赖基类,那么id表达式是未知专门化的成员;请参见下文。—尾注]

6如果名称是,则它是未知专业化的成员

  • […]
  • 一个id表达式,表示类成员访问表达式中的成员,其中
    • 对象表达式的类型是当前实例化,当前实例化至少有一个依赖基类,并且名称id表达式的查找找不到属于当前实例化或其非依赖基类;或

7类似地,如果对象表达式的类型为当前实例化的类成员访问表达式中的id表达式没有指代当前实例化的成员或未知专业化的成员,则即使包含成员访问表达式的模板没有实例化,程序也是格式错误的;无需诊断。

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

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

短语"两阶段查找"被广泛误解,甚至在专家中也是如此。C++17表示两个

这些名称是未绑定的,并且在模板定义的上下文和实例化点的上下文中的模板实例化点(17.6.4.1)处查找。

(在[温度dep]/1中)和

在解析依赖名称时,会考虑来自以下来源的名称:
-在模板定义点可见的声明
-来自与函数参数类型相关联的命名空间的声明实例化上下文(17.6.4.1)和定义上下文。

(在[temp.dep.res]/1中),每一个似乎都表明这是两个阶段。然而,解释这个(非官方)短语的更好方法是,查找是完成的

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

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

有关措辞最近已得到澄清;它现在只说

[注意:对于使用关联命名空间([basic.lookup.argdep])的查找部分,在模板实例化上下文中找到的函数声明通过此查找找到,如[basic.lookup.argdep]中所述。--结束注释]

提醒注意第二阶段的差异。尽管如此,令人困惑的是,它使用依赖名称只是指代不合格的依赖名称/运算符([temp.dep]/2)。像Derived::foo(来自this->)这样的名称显然是依赖的;作为限定名称,它们不受ADL约束,但在实例化过程中仍会查找它们——这允许从依赖的基中找到结果。(你的例子有一个非依赖的基础,但那也是可用的。)