在模板化的派生类中,为什么我需要使用“this->”来限定基类成员名称。里面的成员函数? [英] In a templated derived class, why do I need to qualify base class member names with "this->" inside a member function?

查看:368
本文介绍了在模板化的派生类中,为什么我需要使用“this->”来限定基类成员名称。里面的成员函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

当我调查Qt的源代码时,我看到trolltech的家伙明确使用这个关键字访问析构函数中的一个字段。


$ b b

  inline〜QScopedPointer()
{
T * oldD = this-> d;
清理:: cleanup(oldD);
this-> d = 0;
}

那么,这个用法有什么意义呢?是否有任何好处?



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



QScopedPointer类

的一部分, a>定义:

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


解决方案



考虑使用模板基类创建一个模板类 Derived

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

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

类型 Derived< T> 是一种取决于 T 的类型。因此这个有一个依赖类型。因此 this-> d 会使 d 一个依赖名称。依赖名称在模板定义的上下文中作为非依赖名称和在实例化的上下文中查找。



没有 this-> ; ,名称 d 将只被查找为非依赖名称,而不能被找到。



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

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



Qanswer(具体答案)



code> d 是一个 QScopedPointer 的成员。它不是继承的成员。 this-> 在这里不需要。



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

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

所以 this-> a href =http://qt.gitorious.org/qt/qt/blobs/4.7/src/corelib/tools/qscopedpointer.h#line198>此处:

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

很容易看出,放置



了解原因



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

  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 ofdfound nothing

f(this); //查找f找不到任何东西
//稍后会找到f
}
};

除了标准说的之外还有一个原因:模板中的名称绑定原因。



当模板被实例化时,模板可以具有迟到的名称:例如 f $ c> f(this)。在 Derived2 :: f()定义时,没有变量,函数或类型名称 f 编译器。此时 f 可以引用的已知实体集为空。这不是一个问题,因为编译器知道它将稍后查找 f 作为函数名或模板函数名。



OTOH,编译器不知道如何处理 d ;它不是一个(调用)函数名。没有办法对非(调用)函数名称进行后期绑定。



现在,所有这些可能看起来像编译时模板多态性的基本知识。真正的问题似乎是:为什么 d 绑定到基础< T> :: d 时间?



真正的问题是在模板定义时没有 Base< T> :: d 因为当时没有完成类型 Base< T> 基本< T> 但未定义!您可能会问:这是什么:

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

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



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

  template< typename T& 
class Base;

。无法在类模板中查找名称!但只有在模板专门化(实例化)。模板是制作模板专业化的工厂,模板不是模板专业化的集合。编译器可以在中查找 d 用于任何特定类型 T ,但不能在类模板 Base
查找 d 。直到确定类型 T Base< T> :: d 仍然是抽象 ; T> :: d ;只有当已知类型 T 时, Base< T> :: d 开始引用<$这样做的结果是类模板

Derived2 有一个完整的基类 Base0 ,但是一个不完整的(转发声明)基类 Base 。只有已知类型 T ,模板类(类模板的特殊化) Derived2< T>



你现在可以看到:

 

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

实际上是基类规范模板规范),它遵循与模板中的基类规范不同的规则。



注:
读者可能已经注意到我已经组成了几个短语在解释的结尾。



这是非常不同的:这里 d Derived< T& 和派生的< T> 是相关的,因为 T 是模板参数。



另一个解决方案是:

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

这是等同的。



认为在 Derived< T> 的定义内,将 Derived< T> 作为已知完整类的处理有时作为一个未知类,有些时候不一致,那么你是对的。


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?

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

A part of QScopedPointer class definition:

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

解决方案

C++ answer (general answer)

Consider a template class Derived with a template base class:

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

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

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.

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

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 (specific answer)

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

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>

so this-> is necessary here:

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

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

Understand the reason

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.

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, 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.

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?

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;

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.

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.

You now see that:

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.

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.

Yet another solution is:

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

This is equivalent.

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.

这篇关于在模板化的派生类中,为什么我需要使用“this-&gt;”来限定基类成员名称。里面的成员函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆