Abgeleiteter Zugriff der Vorlagenklasse auf Mitgliederdaten der Basisklasse

71

Diese Frage ist eine Weiterentwicklung der in diesem Thread gestellten Frage .

Verwenden der folgenden Klassendefinitionen:

template <class T>
class Foo {

public:
    Foo (const foo_arg_t foo_arg) : _foo_arg(foo_arg)
    {
        /* do something for foo */
    }
    T Foo_T;        // either a TypeA or a TypeB - TBD
    foo_arg_t _foo_arg;
};

template <class T>
class Bar : public Foo<T> {
public:
    Bar (const foo_arg_t bar_arg, const a_arg_t a_arg)
    : Foo<T>(bar_arg)   // base-class initializer
    {

        Foo<T>::Foo_T = T(a_arg);
    }

    Bar (const foo_arg_t bar_arg, const b_arg_t b_arg)
    : Foo<T>(bar_arg)
    {
        Foo<T>::Foo_T = T(b_arg);
    }

    void BarFunc ();

};

template <class T>
void Bar<T>::BarFunc () {
    std::cout << _foo_arg << std::endl;   // This doesn't work - compiler error is: error: ‘_foo_arg’ was not declared in this scope
    std::cout << Bar<T>::_foo_arg << std::endl;   // This works!
}

Wenn ich auf die Mitglieder der Basisklasse der Vorlagenklasse zugreife, muss ich die Mitglieder anscheinend immer explizit mit der Syntax des Vorlagenstils von qualifizieren Bar<T>::_foo_arg. Gibt es eine Möglichkeit, dies zu vermeiden? Kann eine 'using'-Anweisung / Direktive in einer Vorlagenklassenmethode ins Spiel kommen, um den Code zu vereinfachen?

Bearbeiten:

Das Scope-Problem wird behoben, indem die Variable mit dieser-> Syntax qualifiziert wird.

Shamster
quelle

Antworten:

78

Mit können Sie this->klarstellen, dass Sie sich auf ein Mitglied der Klasse beziehen:

void Bar<T>::BarFunc () {
    std::cout << this->_foo_arg << std::endl;
}

Alternativ können Sie auch " using" in der Methode verwenden:

void Bar<T>::BarFunc () {
    using Bar<T>::_foo_arg;             // Might not work in g++, IIRC
    std::cout << _foo_arg << std::endl;
}

Dies macht dem Compiler klar, dass der Mitgliedsname von den Vorlagenparametern abhängt, sodass er an den richtigen Stellen nach der Definition dieses Namens sucht. Weitere Informationen finden Sie auch in diesem Eintrag in C ++ Faq Lite .

etw
quelle
3
Der Link zur FAQ ist sehr nützlich: Er zeigt auch, wo dieses Problem unsichtbar unerwünschtes Verhalten verursachen kann.
xtofl
3
Irgendeine Idee, warum das so ist? (Die FAQ beantwortet dies nicht vollständig)
Catskul
29

Hier ist die Basisklasse keine nicht abhängige Basisklasse (dh eine mit einem vollständigen Typ, der ohne Kenntnis der Vorlagenargumente bestimmt werden kann) und _foo_argist ein nicht abhängiger Name. Standard C ++ besagt, dass nicht abhängige Namen in abhängigen Basisklassen nicht nachgeschlagen werden.

Um den Code zu korrigieren, reicht es aus, den Namen _foo_argabhängig zu machen, da abhängige Namen nur zum Zeitpunkt der Instanziierung nachgeschlagen werden können und zu diesem Zeitpunkt die genaue Basisspezialisierung bekannt ist, die untersucht werden muss. Zum Beispiel:

// solution#1
std::cout << this->_foo_arg << std::endl;

Eine Alternative besteht darin, eine Abhängigkeit unter Verwendung eines qualifizierten Namens einzuführen:

// solution#2
std::cout << Foo<T>::_foo_arg << std::endl;

Bei dieser Lösung ist Vorsicht geboten, denn wenn der nicht qualifizierte, nicht abhängige Name zur Bildung eines virtuellen Funktionsaufrufs verwendet wird, verhindert die Qualifizierung den virtuellen Aufrufmechanismus und die Bedeutung des Programms ändert sich.

Und Sie können einen Namen aus einer abhängigen Basisklasse in die abgeleitete Klasse einmal einbringen, indem Sie using:

// solution#3
template <class T>
class Bar : public Foo<T> {
public:
    ...
    void BarFunc ();
private:
    using Foo<T>::_foo_arg;
};

template <class T>
void Bar<T>::BarFunc () {
    std::cout << _foo_arg << std::endl;   // works
}
songyuanyao
quelle
1

Scheint in Visual C ++ 2008 einwandfrei zu funktionieren. Ich habe einige Dummy-Definitionen für die von Ihnen genannten Typen hinzugefügt, aber keine Quelle angegeben. Der Rest ist genau so, wie Sie es ausdrücken. Dann eine Hauptfunktion zu erzwingen BarFunc, instanziiert und aufgerufen zu werden.

#include <iostream>

class streamable {};
std::ostream &operator<<(std::ostream &os, streamable &s) { return os; }

class foo_arg_t : public streamable {};
class a_arg_t : public streamable {};
class b_arg_t : public streamable  {};

template <class T>
class Foo {

public:
    Foo (const foo_arg_t foo_arg) : _foo_arg(foo_arg)
    {
        /* do something for foo */
    }
    T Foo_T;        // either a TypeA or a TypeB - TBD
    foo_arg_t _foo_arg;
};

template <class T>
class Bar : public Foo<T> {
public:
    Bar (const foo_arg_t bar_arg, const a_arg_t a_arg)
    : Foo<T>(bar_arg)   // base-class initializer
    {

        Foo<T>::Foo_T = T(a_arg);
    }

    Bar (const foo_arg_t bar_arg, const b_arg_t b_arg)
    : Foo<T>(bar_arg)
    {
        Foo<T>::Foo_T = T(b_arg);
    }

    void BarFunc ();

};

template <class T>
void Bar<T>::BarFunc () {
    std::cout << _foo_arg << std::endl; 
    std::cout << Bar<T>::_foo_arg << std::endl;   
}

int main()
{
    Bar<a_arg_t> *b = new Bar<a_arg_t>(foo_arg_t(), a_arg_t());
    b->BarFunc();
}
Daniel Earwicker
quelle
g ++ gibt viele Fehler in Bezug auf die Definitionen oben aus. Das Bereichsproblem bleibt jedoch weiterhin bestehen: "Fehler: '_foo_arg' wurde in diesem Bereich nicht deklariert", da _foo_arg zum ersten Mal in der BarFunc () - Definition aufgerufen wurde.
Shamster
Meinst du, meine Dummy-Typdeklarationen geben dir Fehler auf gcc?
Daniel Earwicker
Ja, die Dummy-Typen oben, aber der Bereichsfehler bleibt auch bestehen.
Shamster
Ich glaube, dass g ++ in Bezug auf Ihr ursprüngliches Problem richtig sein kann. Der IBM-Compiler hat die gleiche Aufregung ausgelöst, IIRC.
Daniel Earwicker
Entschuldigung, ich hatte einen Testcode hinzugefügt - das gab mir den Fehler. Der Code, den Sie gepostet haben, wird kompiliert, wenn Sie this -> _ foo_arg anstelle von _foo_arg in BarFunc () verwenden.
Shamster