web-dev-qa-db-fra.com

Accès dérivé de classe de modèle aux données de membre de classe de base

Cette question est un prolongement de celle posée dans ce fil .

Utilisation des définitions de classe suivantes:

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

Lors de l'accès aux membres de la classe de base de la classe de modèles, il semble que je doive toujours qualifier explicitement les membres à l'aide de la syntaxe de style de modèle de Bar<T>::_foo_arg. Y a-t-il un moyen d'éviter cela? Une instruction/directive "using" peut-elle entrer en jeu dans une méthode de classe de modèle pour simplifier le code?

Éditer:

Le problème de portée est résolu en qualifiant la variable avec cette syntaxe->.

56
Shamster

Vous pouvez utiliser this-> pour indiquer clairement que vous faites référence à un membre de la classe:

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

Alternativement, vous pouvez également utiliser "using" dans la méthode:

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

Cela rend clair pour le compilateur que le nom du membre dépend des paramètres du modèle afin qu'il recherche la définition de ce nom aux bons endroits. Pour plus d'informations, voir également cette entrée dans la C++ Faq Lite .

61
sth

Ici, la classe de base n'est pas une classe de base non dépendante (ce qui signifie une avec un type complet qui peut être déterminé sans connaître les arguments du modèle), et _foo_arg est un nom non dépendant. Le C++ standard indique que les noms non dépendants ne sont pas recherchés dans les classes de base dépendantes.

Pour corriger le code, il suffit de faire le nom _foo_arg dépendante car les noms dépendants ne peuvent être recherchés qu'au moment de l'instanciation, et à ce moment la spécialisation de base exacte qui doit être explorée sera connue. Par exemple:

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

Une alternative consiste à introduire une dépendance en utilisant un nom qualifié:

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

Il faut faire attention à cette solution, car si le nom non dépendant non qualifié est utilisé pour former un appel de fonction virtuelle, la qualification inhibe le mécanisme d'appel virtuel et la signification du programme change.

Et vous pouvez apporter une fois le nom d'une classe de base dépendante dans la classe dérivée par 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
}
23
songyuanyao

Semble fonctionner correctement dans Visual C++ 2008. J'ai ajouté quelques définitions factices pour les types que vous avez mentionnés, mais sans donner de source. Le reste est exactement comme vous l'avez dit. Ensuite, une fonction principale pour forcer BarFunc à être instanciée et appelée.

#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();
}
1
Daniel Earwicker