2017-07-14 9 views
2

comment définir une classe de modèle héritée de la classe de modèle? Je veux envelopper std::queue et std::priority_queue à une classe de base. Dans mon cas, c'est LooperQueue. J'utilise ainsi StdQueueauto queue = new StdQueue<LooperMessage *>().Classe de modèle C++ hériter

ma classe définir compilateur se plaindre

journal des erreurs:

In file included from /Users/rqg/ASProjects/PboTest/muses/src/main/cpp/Painter.cpp:10: 
    /Users/rqg/ASProjects/PboTest/muses/src/main/cpp/util/StdQueue.h:14:5: error: unknown type name 'size_type'; did you mean 'size_t'? 
     size_type size() override; 
     ^~~~~~~~~ 
     size_t 
    /Users/rqg/Library/Android/sdk/ndk-bundle/toolchains/llvm/prebuilt/darwin-x86_64/lib64/clang/5.0.300080/include/stddef.h:62:23: note: 'size_t' declared here 
    typedef __SIZE_TYPE__ size_t; 
         ^
    In file included from /Users/rqg/ASProjects/PboTest/muses/src/main/cpp/Painter.cpp:10: 
    /Users/rqg/ASProjects/PboTest/muses/src/main/cpp/util/StdQueue.h:16:5: error: unknown type name 'reference' 
     reference front() override; 
    ^
    /Users/rqg/ASProjects/PboTest/muses/src/main/cpp/util/StdQueue.h:20:21: error: unknown type name 'value_type'; did you mean 'ARect::value_type'? 
     void push(const value_type &x) override; 
         ^~~~~~~~~~ 
         ARect::value_type 
    /Users/rqg/Library/Android/sdk/ndk-bundle/sysroot/usr/include/android/rect.h:44:21: note: 'ARect::value_type' declared here 
     typedef int32_t value_type; 

Code:

#ifndef PBOTEST_LOOPERQUEUE_H 
#define PBOTEST_LOOPERQUEUE_H 

#include <queue> 
#include <cstdlib> 

template<typename Tp, typename Sequence = std::deque<Tp> > 
class LooperQueue { 
public: 

    typedef typename Sequence::value_type    value_type; 
    typedef typename Sequence::reference     reference; 
    typedef typename Sequence::const_reference   const_reference; 
    typedef typename Sequence::size_type     size_type; 
    typedef   Sequence       container_type; 


    virtual size_type size() = 0; 

    virtual reference front() = 0; 

    virtual void pop()= 0; 

    virtual void push(const value_type &x) = 0; 
}; 


#endif //PBOTEST_LOOPERQUEUE_H 
#ifndef PBOTEST_STDQUEUE_H 
#define PBOTEST_STDQUEUE_H 


#include "LooperQueue.h" 

template<typename Tp, typename Sequence = std::deque<Tp> > 
class StdQueue : public LooperQueue<Tp, Sequence> { 
public: 
    size_type size() override; 

    reference front() override; 

    void pop() override; 

    void push(const value_type &x) override; 

public: 


private: 
    std::queue<Tp, Sequence> mQueue; 
}; 


#endif //PBOTEST_STDQUEUE_H 
+2

De plus, je recommande d'utiliser 'using' au lieu de typedef. La syntaxe est plus claire et je pense qu'elle peut parfois donner de meilleurs messages d'erreur. – xaxxon

+1

En outre, vous n'êtes pas autorisé à utiliser des noms commençant par un trait de soulignement suivi d'une lettre majuscule ou de tout nom commençant par deux caractères de soulignement. _Séquence, par exemple, n'est pas autorisée. https://stackoverflow.com/a/228797/493106 – xaxxon

+2

@xaxxon un identifiant avec deux caractères de soulignement consécutifs * n'importe où * dans celui-ci n'est pas autorisé –

Répondre

2

Le problème ici est que la classe de base LooperQueue est une classe de base dépendante, qui dépend du paramètre du modèle Tp et Sequence, alors son type complet peut » t être déterminé sans connaître les arguments du modèle.La norme C++ indique que les noms non dépendants (comme size_type, reference et value_type) ne seront pas recherchés dans les classes de base dépendantes.

Pour corriger le code, il suffit de rendre les noms qualifiés par le nom de classe de base; alors ces noms ne peuvent être recherchés qu'au moment de l'instanciation, et à ce moment-là la spécialisation de base exacte qui doit être explorée sera connue. par exemple. L'affichage des erreurs du compilateur sans les noms de fichiers et les numéros de ligne pue

template<typename _Tp, typename _Sequence = std::deque<_Tp> > 
class StdQueue : public LooperQueue<_Tp, _Sequence> { 
public: 
    typename LooperQueue<_Tp, _Sequence>::::size_type size() override; 
    typename LooperQueue<_Tp, _Sequence>::reference front() override; 
    void pop() override; 
    void push(const typename LooperQueue<_Tp, _Sequence>::value_type &__x) override; 
private: 
    std::queue<_Tp, _Sequence> mQueue; 
}; 
+0

puis-je définir 'StdQueue' dans le fichier' * .h' et implémenter 'StdQueue' dans le fichier' * .cpp'? –

+1

@Fantasy_RQG Voir [Pourquoi les modèles ne peuvent-ils être implémentés que dans le fichier d'en-tête?] (Https://stackoverflow.com/q/495021/3309790) – songyuanyao

3

Je vais utiliser un exemple plus simple qui donne vous la même erreur , Envisager une base avec juste un alias défini et une sous-classe qui tente de l'utiliser:

template <typename T> 
class Base { 
public: 
    using value_type = T; 
}; 

template <typename T> 
class Derived : public Base<T> { 
    value_type func(); // error 
}; 

En raison de la nature fou de modèles, le compilateur ne peut pas savoir ce que value_type est à ce stade. Il faut dire que ce qu'il vient de la classe de base soit en le qualifiant:

template <typename T> 
class Derived : public Base<T> { 
    typename Base<T>::value_type func(); 
}; 

ou dire au compilateur avec une déclaration using que vous avez l'intention d'utiliser un alias de type classe de base

template <typename T> 
class Derived : public Base<T> { 
    using typename Base<T>::value_type; 
    value_type func(); 
}; 

Le compilateur ne peut pas réellement savoir que Base<T> contient un value_type jusqu'à ce qu'il sache ce que T est et instancie le modèle. Pourquoi ne peut-il pas simplement regarder le modèle de base? - une telle chose serait théoriquement possible, mais elle ne sait pas quelles spécialisations seront disponibles. Si vous aviez un autre

template<> 
class Base<int> {}; 

Alors Derived<int> faudrait chercher value_type ailleurs dans son champ d'application, et c'est ce qu'il fait dans votre code d'origine. Il essaie de trouver un value_type et échoue. Ce comportement peut conduire à des résultats surprenants:

using value_type = char; 
template <typename T> 
class Derived : public Base<T> { 
    value_type func(); // this is the global value_type = char, always 
}; 

Pour une explication plus de sol sur des sujets connexes see this blog post

1

La plupart, sinon la totalité, de vos erreurs de compilation sont dues aux types dans votre classe de base ne pas être examiné pendant la recherche de nom de votre classe dérivée. La norme C++ indique que vous devez qualifier complètement les noms de type (voir this question). En d'autres termes, les types dans votre classe de base de modèle ne sont pas visibles à partir de votre classe dérivée sans être entièrement qualifiés. Ci-dessous un exemple simple qui compile et fonctionne avec g++-6.3.0 -std=c++14:

#include <iostream> 
#include <deque> 

using namespace std; 

template <typename T, typename S = deque<T> > 
class Base 
{ 
public: 
    typedef typename S::size_type size_type; 

    virtual size_type size() = 0; 
}; 


template <typename T, typename S = deque<T> > 
class MyClass : public Base<T, S> 
{ 
public: 
    // type name has to be fully qualified 
    typedef typename Base<T,S>::size_type size_type; 

    // you could use "typename Base<T,S>::size_type" here instead 
    size_type size() override { return 0; } 
}; 


int main() 
{ 
    MyClass<int> c; 
    cout << c.size() << endl; 
} 
+0

'typedef typename Base :: size_type size_type;' peut être accompli avec 'using typename Base :: size_type;' –

+0

Oui, tous les 'typedef's peuvent être accomplis avec' using' (déclarations d'alias), et je préfère utiliser des déclarations d'alias au lieu de 'typedef'. Cependant, j'ai décidé de quitter le 'typedef's pour rendre l'exemple plus proche du code original de Fantasy_RQG. –

+0

votre code fonctionne très bien, mais je ne peux pas diviser la fonction définir en déclaration et définition. Est-ce qu'il y a un moyen de faire ça ? –