11

以下代码可以无错误地编译:

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

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

this如果类模板具有类型相关的基础,我可以理解这是类型相关的,例如:

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<T>可能有一个名为的成员whatever)。相反,this->whatever在第一个示例中,只要在f某处使用成员函数,我就没有看到任何合法的潜力。

那么,this->whatever在第一个例子中的某些时候可能是合法的吗?如果不是,在这种情况下是否还有其他原因this应该被视为类型相关的表达式?

4

3 回答 3

4

您的代码“格式错误,不需要诊断”,因为A::f. 事实上,规范说this->whatever它既不是未知特化的成员(因为没有依赖的基类),也不是当前实例化的成员(因为它没有在非依赖的基类中声明,也没有在类模板中声明本身)。这还使您的代码无效,并且再次不需要诊断(但允许)。这在https://stackoverflow.com/a/17579889/34509有更详细的解释

this是类型相关的,因为您还不知道定义中的模板参数值。所以例如SomeOtherTemplate<decltype(*this)>不能立即解决,而是需要等到类模板this实例化(所以你需要一个typenamebefore SomeOtherTemplate<decltype(*this)>::type)。

但是,仅仅因为this依赖于类型,并不意味着它this->whatever也是。如上所述,规范有工具可以正确地将其归类为无效,实际上也不依赖this->whatever类型。它说

如果类成员访问表达式 ([expr.ref]) 引用当前实例化的成员并且所引用成员的类型是依赖的,或者类成员访问表达式引用未知的成员,则类成员访问表达式 ([expr.ref]) 是类型相关的专业化。

于 2016-08-30T06:47:17.143 回答
0

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

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

该语句不应该编译并且即使具有依赖于类型的基类this = 1;也无法修复。但是,在函数实例化A<T>之前,编译器不会抱怨。A<T>::f()

正如Johannes Schaub - litb已经回答的那样,这可能是“不需要诊断”的情况。

于 2016-08-30T06:27:04.107 回答
0

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

$14.6/9 名称解析 [temp.res]

在查找模板定义中使用的名称声明时,通常的查找规则([basic.lookup.unqual]、[basic.lookup.argdep])用于非依赖名称。依赖模板参数的名称查找被推迟到知道实际的模板参数([temp.dep])。

意图是,如果名称取决于模板参数,则信息不充分,直到知道实际的模板参数。编译器不会区分依赖名称的类型(由this或其他人组成),不会检查类是否具有依赖基类等细节。结果可能不会像您显示的示例代码那样改变,但它只是推迟名称查找直到类型已知,以做出最准确的决定。

于 2016-08-30T06:55:21.250 回答