关于c ++:为什么`this`是一个依赖于类型的表达式,即使模板类没有基类?

Why `this` is a type-dependent expression even if the template class has no base class?

可以编译以下代码而不出错:

1
2
3
4
5
6
template <typename T> struct A {
    void f() { this->whatever; } // whatever is not declared before
};
int main() {
    A<int> a;
}

我知道这是因为this是一个依赖类型的表达式,它使得whatever的名称查找被推迟到实际的模板参数已知为止。由于成员函数f()在这种情况下从未使用过,因此不存在A::f的实例化,也不执行whatever的名称查找。

我可以理解,如果类模板有一个依赖于类型的基,那么this是依赖于类型的,比如:

1
2
3
4
5
6
7
template <typename T> struct B { T whatever; };
template <typename T> struct A : B<T> {
    void f() { this->whatever; }
};
int main() {
    A<int> a;
}

在解析模板类A的定义时,不可能知道它的基类型,这使得this->whatever可能是合法的(B可能有一个名为whatever的成员)。相反,在第一个例子中,只要在某个地方使用成员函数f,我就看不到this->whatever在第一个例子中是合法的。

那么,在第一个例子中,this->whatever在某些方面是合法的吗?如果没有,那么在这种情况下,是否还有其他原因可以将this视为依赖类型的表达式?


您的代码"格式不正确,不需要诊断",因为A::f从来没有有效的专门化。实际上,规范说this->whatever既不是未知专门化的成员(因为没有依赖的基类),也不是当前实例化的成员(因为它没有在非依赖的基类中声明,也没有在类模板本身中声明)。此外,这会使您的代码无效,并且同样不需要诊断(但允许)。这在https://stackoverflow.com/a/17579889/34509中有更详细的解释。

this依赖于类型,因为您还不知道定义中的模板参数值。例如,SomeOtherTemplate不能立即解决,但需要等到this的类模板被实例化(所以在SomeOtherTemplate::type之前需要一个typename)。

然而,仅仅因为this是依赖于类型的,并不意味着this->whatever也是依赖于类型的。如上所述,规范有工具将其正确分类为无效,实际上也不会使this->whatever类型依赖。它说

A class member access expression ([expr.ref]) is type-dependent if the expression refers to a member of the current instantiation and the type of the referenced member is dependent, or the class member access expression refers to a member of an unknown specialization.


这是有关从属名称的名称查找规则。

$14.6/9名称解析[温度]:

When looking for the declaration of a name used in a template definition, the usual lookup rules ([basic.lookup.unqual], [basic.lookup.argdep]) are used for non-dependent names. The lookup of names dependent on the template parameters is postponed until the actual template argument is known ([temp.dep]).

其目的是,如果名称依赖于模板参数,则信息不足,直到知道实际的模板参数为止。编译器不会区分依赖名称的类型(由this或其他名称组成),也不会像检查类是否有依赖基类那样检查细节。结果可能不会像您显示的示例代码那样发生变化,但它只会推迟名称查找,直到知道类型,从而做出最准确的决定。


您的示例可以进一步简化:

1
2
3
4
5
6
template <typename T> struct A {
    void f() { this = 1; }
};
int main() {
    A<int> a;
}

声明this = 1;不应该编译,即使A有一个依赖类型的基类,它也不能被修复。但是,直到函数A::f()被实例化之后,编译器才会抱怨。

正如JohannesSchaub-Litb已经回答的那样,这可能是一种"不需要诊断"的情况。