2010-07-29 6 views
6

J'ai une classe basé sur un modèle C++ qui expose un certain nombre de méthodes, par exempleméthodes Ajout à la spécialisation du modèle

template<int X, int Y> 
class MyBuffer { 
public: 
    MyBuffer<X,Y> method1(); 
}; 

Maintenant, je veux exposer d'autres méthodes à cette classe si X == Y. Je l'ai fait par le sous-classement MyBuffer,

template<int X> 
class MyRegularBuffer : public MyBuffer<X,X> { 
public: 
    MyRegularBuffer method2(); 
}; 

Maintenant, le problème est que je veux être en mesure de faire par exemple

MyRegularBuffer<2> buf = ... 
MyRegularBuffer<2> otherBuf = buf.method1().method2(); 

Mais je ne suis pas sûr de savoir comment accomplir cela. J'ai essayé de penser à des constructeurs de copie, des opérateurs de conversion, etc., mais mes compétences C++ sont malheureusement un peu rouillées.

EDIT: Je dois ajouter que la création de ces objets est relativement pas cher (et aussi, il ne se produira pas beaucoup), ce qui signifie qu'il serait correct de faire quelque chose comme ceci:

MyRegularBuffer<2> buf = ... 
MyRegularBuffer<2> temp = buf.method1(); // Implicit conversion 
MyRegularBuffer<2> otherBuf = temp.method2(); 

Le question est alors, comment puis-je définir la conversion comme ça. L'opérateur de conversion doit être dans MyBuffer, je pense, mais je veux qu'il soit disponible seulement si X == Y.

+1

complètement incompréhensible. Par exemple, vous parlez de "l'opérateur de conversion", mais il n'y en a pas. Postez du vrai code. –

+0

@Neil, je pense que l'utilisateur a posé une véritable question au mieux de leurs capacités. Et je pense que je le comprends un peu. –

+0

@Aaron Dans ce cas, merci de partager votre compréhension. –

Répondre

5

Vous n'avez pas besoin d'une catégorie distincte pour représenter le comportement spécial. Spécialisation partielle vous permet de traiter certains des cas MyBuffer < X, Y > spécialement et leur donner des méthodes supplémentaires.

Gardez votre déclaration originale de MonBuffer < X, Y > et ajoutez ceci:

template<int Y> 
class MyBuffer<Y, Y> { 
public: 
    MyBuffer<Y,Y> method1(); 
    MyBuffer<Y,Y> method2(); 
}; 

MyBuffer<1,2> m12; m12.method2(); // compile fail, as desired, as it doesn't have such a method because 1 != 2 
MyBuffer<2,2> m22; m22.method2(); // compile success 

Edit: mes dernières lignes ne sont pas très utiles après tout, comme l'a souligné Georg dans les commentaires, donc je les ai supprimés.

+1

Le seul inconvénient est que method1() doit être ré-implémenté dans MyBuffer , sinon le compilateur se plaindra d'une méthode inconnue lorsque vous essayez d'appeler MyBuffer :: method1(). AFAIK, il n'y a aucun moyen d'avoir MyBuffer :: method1() déléguer son implémentation à MyBuffer :: method1() sans spécifier de paramètres de template différents où X! = Y. –

+0

Dériver de 'MyBuffer' ne fonctionnerait pas - il doesn Je ne connais pas la classe dérivée et je ne peux donc pas retourner le type approprié de 'method1()'. –

+0

@Georg, je ne comprends pas. Pouvez-vous être plus précis sur la ligne qui va échouer? J'ai compilé mon code et ça marche. Mais je dois admettre que j'ai copié incorrectement la toute dernière ligne à propos de "class MyRegularBuffer". Mettra à jour maintenant. –

1

Il est possible de faire ce que vous voulez si method1 et method2 renvoient une référence à *this. Sinon, vous devrez effectuer une conversion ou créer un method1 virtuel.

+0

Même si method1() renvoie cette valeur, vous ne pouvez toujours pas appeler method2() à partir de sa valeur de retour, car method2() ne fait pas partie de l'interface de MyBuffer . – wilhelmtell

+0

Pouvez-vous élaborer sur l'approche de la méthode virtuelle? Je ne suis pas sûr d'avoir eu ça ... Le problème auquel je suis confronté est le contrat, l'implémentation serait la même (les objets sont identiques en mémoire si X == Y, donc je pourrais juste faire une réinterprétation). – Krumelur

+0

wilhelmtell, oui, exactement ce que je voulais dire. Vous étiez plus rapide :) – Krumelur

1

L'astuce est d'avoir un MyRegularBuffer::method1 qui appelle MyBuffer::method1, puis un moyen de convertir l'MyBuffer<X,X> résultant en un MyRegularBuffer<X>:

template<int X> 
class MyRegularBuffer : public MyBuffer<X,X> 
{ 
public: 

    MyRegularBuffer<X>() 
    {} 

    MyRegularBuffer<X>(MyBuffer<X,X>) 
    { 
    // copy fields, or whatever 
    } 

    MyRegularBuffer<X> method2(); 

    MyRegularBuffer<X> method1() 
    { 
    MyRegularBuffer<X> ret(MyBuffer<X,X>::method1()); 
    return(ret); 
    } 
}; 
+0

Merci. Oui, c'est une bonne idée, mais dans mon cas cela n'ajoute rien à la solution de @Aaron McDaid, en ce sens que je dois à nouveau implémenter method1 (method1 est en fait un certain nombre de méthodes). – Krumelur

3

Je vais pour CRTP ici:

template<int X, int Y, class Derived> 
struct MyBufferBase { 
    // common interface: 
    Derived& method1() { return *static_cast<Derived*>(this); } 
}; 

template<int X, int Y> 
struct MyBuffer : MyBufferBase<X, Y, MyBuffer<X,Y> > { 
    // basic version 
}; 

template<int X> 
struct MyRegularBuffer : MyBufferBase<X, X, MyRegularBuffer<X> > { 
    // extended interface: 
    MyRegularBuffer& method2() { return *this; } 
}; 
+0

+1 Merci de me le rappeler. –

+0

Merci pour cela. C'est absolument une bonne idée. – Krumelur

+0

J'aurais marqué comme réponse si j'aurais pu donner la note à plus d'une réponse, mais en ce moment la solution d'Aaron McDaid tombe assez bien, puisque mes méthodes sont assez fines (c'est une classe d'emballage). – Krumelur

Questions connexes