Warum muss ich in einer auf Vorlagen basierenden abgeleiteten Klasse die Elementnamen der Basisklasse mit „this->“ innerhalb einer Elementfunktion qualifizieren?

Lesezeit: 5 Minuten

Warum muss ich in einer auf Vorlagen basierenden abgeleiteten Klasse
benutzt

Während ich den Quellcode von Qt untersuche, habe ich gesehen, dass Trolltech-Jungs ihn ausdrücklich verwenden this Schlüsselwort für den Zugriff auf ein Feld im Destruktor.

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

Also, was ist der Sinn dieser Verwendung? Gibt es Vorteile?

Bearbeiten: Für diejenigen, die dafür stimmen, diese Frage zu schließen, vermute ich, dass diese Verwendung für einige Klassenvererbungsfälle gilt

Ein Teil von QScopedPointer-Klasse Definition:

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

Warum muss ich in einer auf Vorlagen basierenden abgeleiteten Klasse
Neugieriger

C++-Antwort (allgemeine Antwort)

Betrachten Sie eine Vorlagenklasse Derived mit einer Template-Basisklasse:

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

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

this Typ hat Derived<T>ein Typ, der davon abhängt T. Damit this hat einen abhängigen Typ. Damit this->d macht d ein abhängiger Name. Abhängige Namen werden im Kontext der Vorlagendefinition als nicht abhängige Namen und im Kontext der Instanziierung nachgeschlagen.

Ohne this->der Name d würde nur als nicht abhängiger Name nachgeschlagen und nicht gefunden werden.

Eine andere Lösung ist zu deklarieren d in der Vorlagendefinition selbst:

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

QAntwort (spezifische Antwort)

d ist ein Mitglied von QScopedPointer. Es ist kein geerbtes Mitglied. this-> ist hier nicht nötig.

OTOH, QScopedArrayPointer ist eine Vorlagenklasse und d ist ein geerbtes Mitglied einer Template-Basisklasse:

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

damit this-> ist notwendig Hier:

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

Es ist leicht zu sehen, dass es einfacher ist, einfach zu setzen this-> überall, überallhin, allerorts.

Verstehen Sie den Grund

Ich denke, es ist nicht allen C++-Benutzern klar, warum Namen in nicht abhängigen Basisklassen, aber nicht in abhängigen Basisklassen nachgeschlagen werden:

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
    }
};

Neben “der Standard sagt es” gibt es einen Grund: Ursache für das Binden von Wegnamen in Vorlagen funktioniert.

Vorlagen können Namen haben, die spät gebunden werden, wenn die Vorlage instanziiert wird: zum Beispiel f in f (this). An der Stelle von Derived2::f() Definition gibt es keinen Variablen-, Funktions- oder Typnamen f dem Compiler bekannt. Die Menge bekannter Entitäten, die f beziehen könnte, ist an dieser Stelle leer. Dies ist kein Problem, da der Compiler weiß, dass er suchen wird f später als Funktionsname oder als Vorlagenfunktionsname.

OTOH, der Compiler weiß nicht, was er damit anfangen soll d; es ist kein (aufgerufener) Funktionsname. Es gibt keine Möglichkeit, eine späte Bindung an Namen von nicht (aufgerufenen) Funktionen durchzuführen.

Nun, all dies mag wie elementares Wissen über die Polymorphie von Vorlagen zur Kompilierzeit erscheinen. Die eigentliche Frage scheint zu sein: Warum nicht d gebunden an Base<T>::d zum Zeitpunkt der Vorlagendefinition?

Das eigentliche Problem ist, dass es keine gibt Base<T>::d zum Zeitpunkt der Vorlagendefinition, weil Es gibt keinen vollständigen Typ Base<T> zu dieser Zeit: Base<T> ist deklariert, aber nicht definiert! Sie fragen sich vielleicht: Was ist damit:

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

es sieht aus wie die Definition eines vollständigen Typs!

Eigentlich sieht es bis zur Instanziierung eher so aus:

template <typename T>
class Base;

zum Compiler. Ein Name kann nicht in einer Klassenvorlage nachgeschlagen werden! Aber nur in einer Template-Spezialisierung (Instanziierung). Die Vorlage ist eine Fabrik, um eine Vorlagenspezialisierung vorzunehmen, Eine Vorlage ist kein Satz von Vorlagenspezialisierungen. Der Compiler kann suchen d in Base<T> für irgendeinen bestimmten Typ Taber es kann nicht gesucht werden d in der Klassenvorlage Base. Bis zu einer Art T festgestellt wird, Base<T>::d bleibt das Abstrakte Base<T>::d; nur beim Typ T ist bekannt, Base<T>::d beginnen, auf eine Variable vom Typ zu verweisen int.

Die Folge davon ist, dass die Klasse Vorlage Derived2 hat eine vollständige Basisklasse Base0 aber eine unvollständige (forward deklarierte) Basisklasse Base. Nur für einen bekannten Typ Tdie “Vorlagenklasse” (Spezialisierungen einer Klassenvorlage) Derived2<T> hat eine vollständige Basisklasse, genau wie jede normale Klasse.

Das siehst du jetzt:

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

ist eigentlich ein Basisklassen-Spezifikationsvorlage (eine Fabrik zum Erstellen von Basisklassenspezifikationen), die anderen Regeln folgt als eine Basisklassenspezifikation innerhalb einer Vorlage.

Anmerkung: Dem Leser ist vielleicht aufgefallen, dass ich am Ende der Erklärung ein paar Sätze erfunden habe.

Das ist ganz anders: hier d ist ein qualifizierter Name in Derived<T>und Derived<T> ist seitdem abhängig T ist ein Vorlagenparameter. Ein qualifizierter Name kann spät gebunden werden, auch wenn es sich nicht um einen (aufgerufenen) Funktionsnamen handelt.

Noch eine andere Lösung ist:

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

Dies ist gleichwertig.

Wenn Sie denken, dass innerhalb der Definition von Derived<T>die Behandlung von Derived<T> manchmal als bekannte vollständige Klasse und manchmal als unbekannte Klasse in widersprüchlicher Weise, nun, Sie haben Recht.

  • Anmerkung des Moderators Kommentare unter dieser Antwort wurden gelöscht, weil sie zu reinem Rauschen degeneriert sind. Bitte verwenden Sie den Stack Overflow Chat für längere Diskussionen und denken Sie daran, höflich zu bleiben.

    – Tim Post

    15. August 2012 um 1:34 Uhr

Ich würde vermuten, dass es sich um eine überladene Verwendung der Cleanup() -Routine handelt. Der übergebene Typ wird explizit durch den Vorlagentyp T gesteuert, der wiederum steuern kann, welche überladene Version von Cleanup() aufgerufen wird.

987520cookie-checkWarum muss ich in einer auf Vorlagen basierenden abgeleiteten Klasse die Elementnamen der Basisklasse mit „this->“ innerhalb einer Elementfunktion qualifizieren?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy