在模板派生类中,为什么我需要使用“this->"来限定基类成员名称?在成员函数中?

2021-12-09 00:00:00 qt templates this c++ name-lookup

当我调查 Qt 的源代码时,我看到 trolltech 人员明确使用 this 关键字来访问析构函数上的字段.

While I investigate source code of Qt I saw that trolltech guys explicitly use this keyword to access a field on destructor.

inline ~QScopedPointer()
{
    T *oldD = this->d;
    Cleanup::cleanup(oldD);
    this->d = 0;
}

那么,这种用法有什么意义呢?有什么好处吗?

So, what's the point of this usage? Are there any benefits?

对于那些投票结束这个问题的人,我怀疑这种用法是用于某些类继承的情况

For those who vote for closing this question, I suspect that this usage is for some class inheritance cases

QScopedPointer 类定义的一部分:

template <typename T, typename Cleanup = QScopedPointerDeleter<T> >
class QScopedPointer

推荐答案

C++ 答案(一般答案)

考虑一个带有模板基类的模板类Derived:

template <typename T>
class Base {
public:
    int d;
};

template <typename T>
class Derived : public Base<T> {
    void f () {
        this->d = 0;
    }
};

this 具有类型 Derived,该类型依赖于 T.所以 this 有一个依赖类型.所以 this->d 使 d 成为依赖名称.依赖名称在模板定义的上下文中作为非依赖名称和实例化上下文进行查找.

this has type Derived<T>, a type which depends on T. So this has a dependent type. So this->d makes d a dependent name. Dependent names are looked-up in the context of the template definition as non-dependent names and in the context of instantiation.

如果没有 this->,名称 d 只会作为非依赖名称被查找,而不会被找到.

Without this->, the name d would only be looked-up as a non-dependent name, and not be found.

另一种解决方案是在模板定义本身中声明d:

Another solution is to declare d in the template definition itself:

template <typename T>
class Derived : public Base<T> {
    using Base::d;
    void f () {
        d = 0;
    }
};

Qanswer(具体答案)

d 是 成员QScopedPointer.它不是继承的成员.this-> 在这里不是必需的.

Qanswer (specific answer)

d is a member of QScopedPointer. It isn't an inherited member. this-> is not necessary here.

OTOH,QScopedArrayPointer 是一个模板类,d 是一个模板基类的继承成员:

OTOH, QScopedArrayPointer is a template class and d is an inherited member of a template base class:

template <typename T, typename Cleanup = QScopedPointerArrayDeleter<T> >
class QScopedArrayPointer : public QScopedPointer<T, Cleanup>

所以 this-> 是必要的 此处:

inline T &operator[](int i)
{
    return this->d[i];
}

很容易看出,将 this-> 放在任何地方更容易.

It's easy to see that it's easier to just put this-> everywhere.

我想所有 C++ 用户都不清楚为什么在非依赖基类中查找名称而不是在依赖基类中查找名称:

I guess it isn't clear to all C++ users why names are looked-up in non-dependent base classes but not in dependent base classes:

class Base0 {
public:
    int nd;
};

template <typename T>
class Derived2 : 
        public Base0, // non-dependent base
        public Base<T> { // dependent base
    void f () {
        nd; // Base0::b
        d; // lookup of "d" finds nothing

        f (this); // lookup of "f" finds nothing
                  // will find "f" later
    }
};

除了标准这么说"之外还有一个原因:模板中名称绑定的工作方式.

There is a reason beside "the standard says so": cause of way name binding in templates works.

模板可以具有后期绑定的名称,当模板被实例化时:例如 f (this) 中的 f.在Derived2::f() 定义点,编译器不知道变量、函数或类型名称f.f 可以引用的一组已知实体此时是空的.这不是问题,因为编译器知道它稍后会查找 f 作为函数名或模板函数名.

Templates can have name that are bound late, when the template is instantiated: for example f in f (this). At the point of Derived2::f() definition, there is no variable, function or type name f known by the compiler. The set of known entities that f could refer to is empty at this point. This isn't a problem because the compiler knows it will lookup f later as a function name, or a template function name.

OTOH,编译器不知道如何处理d;它不是(被调用的)函数名.无法对非(被调用)函数名称进行后期绑定.

OTOH, the compiler doesn't know what to do with d; it isn't a (called) function name. There is no way to do late binding on non-(called) functions names.

现在,所有这些看起来像是编译时模板多态的基本知识.真正的问题似乎是:为什么在模板定义时 d 没有绑定到 Base::d?

Now, all of this may seem like elementary knowledge of compile-time template polymorphism. The real question seems to be: why isn't d bound to Base<T>::d at template definition time?

真正的问题是在模板定义时没有Base::d,因为没有完整的类型Base那个时候:Base 被声明了,但没有定义! 你可能会问:那怎么办:

The real issue is that there is no Base<T>::d at template definition time, because there is no complete type Base<T> at that time: Base<T> is declared, but not defined! You may ask: what about this:

template <typename T>
class Base {
public:
    int d;
};

看起来像是一个完整类型的定义!

it looks like the definition of a complete type!

实际上,直到实例化,它看起来更像是:

Actually, until instantiation, it looks more like:

template <typename T>
class Base;

编译器.不能在类模板中查找名称!但仅限于模板特化(实例化).模板是使模板特化的工厂,模板不是模板特化的集合.编译器可以在 Base 中查找任何特定类型的 T 中的 d,但它不能在类模板Base 中查找d.在确定类型 T 之前,Base::d 仍然是抽象的 Base::d;只有当类型 T 已知时,Base::d 才开始引用 int 类型的变量.

to the compiler. A name cannot be looked-up in a class template! But only in a template specialisation (instantiation). The template is a factory to make template specialisation, a template isn't a set of template specialisation. The compiler can lookup d in Base<T> for any particular type T, but it cannot lookup d in the class template Base. Until a type T is determined, Base<T>::d remains the abstract Base<T>::d; only when type T is known, Base<T>::d start to refer to a variable of type int.

这样做的结果是类模板 Derived2 有一个完整的基类 Base0 但是一个不完整的(前向声明的)基类 <代码>基础.仅对于已知类型 T,模板类"(类模板的特化)Derived2 具有完整的基类,就像任何普通类一样.

The consequence of this is that the class template Derived2 has a complete base class Base0 but an incomplete (forward declared) base class Base. Only for a known type T, the "template class" (specialisations of a class template) Derived2<T> has a complete base classes, just like any normal class.

你现在看到了:

template <typename T>
class Derived : public Base<T> 

实际上是一个基类规范模板(一个制作基类规范的工厂),它遵循与模板内的基类规范不同的规则.

is actually a base class specification template (a factory to make base class specifications) that follows different rules from a base class specification inside a template.

备注:读者可能已经注意到,我在解释的最后编了几句.

Remark: The reader may have noticed that I have made-up a few phrases at the end of the explanation.

这是非常不同的:这里dDerived中的限定名,而Derived是依赖的,因为T 是一个模板参数.限定名称可以是后期绑定的,即使它不是(被调用的)函数名称.

This is very different: here d is a qualified name in Derived<T>, and Derived<T> is dependent since T is a template parameter. A qualified name can be late-bound even if it isn't a (called) function name.

另一种解决方案是:

template <typename T>
class Derived : public Base<T> {
    void f () {
        Derived::d = 0; // qualified name
    }
};

这是等价的.

如果您认为在 Derived 的定义中,将 Derived 视为已知的完整类,有时将其视为未知类有时不一致,嗯,你是对的.

If you think that inside the definition of Derived<T>, the treatment of Derived<T> as a known complete class sometimes and as an unknown class some other times in inconsistent, well, you are right.

相关文章