360 votes

Un modèle de fonction membre d'une classe peut-il être virtuel ?

J'ai entendu dire que les modèles de fonctions membres de classes C++ ne peuvent pas être virtuels. Est-ce bien le cas ?

Si elles peuvent être virtuelles, quel est un exemple de scénario dans lequel on utiliserait une telle fonction ?

15 votes

J'ai été confronté à un problème similaire, et j'ai également appris qu'il est controversé d'être virtuel et modèle en même temps. Ma solution était d'écrire la magie du modèle qui sera commune aux classes dérivées et d'appeler une fonction virtuelle pure qui fait la partie spécialisée. Cette solution est bien sûr liée à la nature de mon problème, et peut donc ne pas fonctionner dans tous les cas.

358voto

sbi Points 100828

Les modèles consistent à ce que le compilateur génère du code à temps de compilation . Les fonctions virtuelles permettent au système d'exécution de déterminer quelle fonction appeler au moment de l'exécution. temps d'exécution .

Une fois que le système d'exécution a compris qu'il devait appeler une fonction virtuelle modélisée, la compilation est terminée et le compilateur ne peut plus générer l'instance appropriée. Par conséquent, vous ne pouvez pas avoir de modèles de fonctions membres virtuelles.

Cependant, il existe quelques techniques puissantes et intéressantes issues de la combinaison du polymorphisme et des modèles, notamment ce que l'on appelle les _effacement de type_ .

2 votes

Je viens de tomber sur ce sujet et j'apprécie mag et vos réponses. J'essaie de ne pas trop espérer, mais l'effacement des caractères pourrait bien être ce que je cherchais.

47 votes

Je ne vois pas de langue raison pour cela, seulement mise en œuvre Les vtables ne font pas partie du langage - c'est juste la façon standard dont les compilateurs implémentent le langage.

26 votes

Virtual functions are all about the run-time system figuring out which function to call at run-time - Désolé, mais c'est une façon de faire plutôt erronée, et assez confuse. C'est juste de l'indirection, et il n'y a pas de "calcul d'exécution" impliqué, on sait pendant la compilation que la fonction à appeler est celle pointée par le n-ième pointeur dans la vtable. Le terme "comprendre" implique qu'il y a des vérifications de type et autres, ce qui n'est pas le cas. Once the run-time system figured out it would need to call a templatized virtual function - le caractère virtuel ou non de la fonction est connu au moment de la compilation.

40voto

pmr Points 30450

Le C++ n'autorise pas les fonctions membres virtuelles des modèles pour le moment. La raison la plus probable est la complexité de son implémentation. Rajendra donne de bonnes raisons pour lesquelles ce n'est pas possible pour le moment, mais cela pourrait être possible avec des modifications raisonnables de la norme. En particulier, déterminer combien d'instanciations d'une fonction modèle existent réellement et construire la table virtuelle semble difficile si l'on considère la place de l'appel de la fonction virtuelle. Les responsables de la normalisation ont tout simplement beaucoup d'autres choses à faire en ce moment et C++1x représente également beaucoup de travail pour les auteurs de compilateurs.

Quand auriez-vous besoin d'une fonction membre modèle ? J'ai rencontré une fois une telle situation où j'ai essayé de refactoriser une hiérarchie avec une classe de base virtuelle pure. C'était un mauvais style pour mettre en œuvre différentes stratégies. Je voulais changer l'argument de l'une des fonctions virtuelles en un type numérique et, au lieu de surcharger la fonction membre et de surcharger chaque surcharge dans toutes les sous-classes, j'ai essayé d'utiliser des fonctions modèles virtuelles (et j'ai dû découvrir qu'elles n'existaient pas).

7 votes

@pmr : Une fonction virtuelle peut être appelée à partir d'un code qui n'existait même pas lorsque la fonction a été compilée. Comment le compilateur déterminerait-il les instances d'une fonction membre de modèle virtuel (théorique) à générer pour un code qui n'existe même pas ?

2 votes

@sbi : Oui, la compilation séparée serait un énorme problème. Je ne suis pas du tout un expert des compilateurs C++ et je ne peux donc pas proposer de solution. Comme avec les fonctions modélisées en général, il devrait être instancié à nouveau dans chaque unité de compilation, non ? Cela ne résoudrait-il pas le problème ?

2 votes

@sbi si vous faites référence aux bibliothèques à chargement dynamique, c'est un problème général avec les classes/fonctions de modèles, pas seulement avec les méthodes de modèles virtuels.

26voto

Mark Essel Points 1082

Les Tables De Fonctions Virtuelles

Commençons par quelques rappels sur la fonction virtuelle tables et comment ils fonctionnent (source):

[20.3] Quelle est la différence entre virtuel et non virtuel les fonctions de membres sont appelés?

Non-membre virtuel fonctions sont résolus de manière statique. Qui est, le fonction membre est sélectionné de manière statique (à la compilation), basé sur la type de l'indicateur (ou de référence) à l'objet.

En revanche, les fonctions membre virtuelles sont résolus de manière dynamique (à au moment de l'exécution). Qui est, la fonction membre est choisi dynamiquement (à au moment de l'exécution) en fonction du type de l'objet, pas le type de l' pointeur/référence à cet objet. Cette opération est appelée "liaison dynamique." La plupart des compilateurs utilisent une variante de la technique suivante: si l' l'objet a une ou plusieurs fonctions virtuelles, le compilateur met un caché pointeur sur l'objet appelé "virtual-pointeur" ou "v-pointeur." Cette v-un pointeur sur un tableau global appelé le "virtuel" table ou "v-table".

Le compilateur crée un v-table pour chaque classe qui possède au moins un une fonction virtuelle. Par exemple, si la classe Cercle a des fonctions virtuelles pour dessiner() et move() et de les redimensionner(), il y a exactement un v-table associée à la classe Cercle, même si il y avait une foule de Cercle des objets, et le v-pointeur de chacun de ces Cercle objets point le Cercle v-table. La v-table elle-même a des pointeurs vers chaque de la fonctions virtuelles de la classe. Par exemple, le Cercle v-table ont trois pointeurs: un pointeur vers le Cercle::draw(), un pointeur vers Cercle::move(), et un pointeur vers le Cercle::resize().

Lors d'un envoi d'une fonction virtuelle, le système d'exécution suit l'objet de la v-pointeur vers la classe v-table, puis suit la fente appropriée dans la v-table pour le code de la méthode.

L'espace-frais généraux de la technique ci-dessus est nominale: un supplément de pointeur par objet (mais seulement pour les objets qu'il sera nécessaire de faire dynamique de liaison), plus un supplément de pointeur par méthode (mais seulement pour virtuel les méthodes). Le temps frais généraux est également assez minime: par rapport à un normal appel de fonction, un appel de fonction virtuelle nécessite deux extra extrait (un pour obtenir la valeur de la v-pointeur, une seconde pour obtenir la l'adresse de la méthode). Rien de tout cela exécution de l'activité qui se passe avec non virtuelle fonctions, car le compilateur résout non virtuelle fonctions exclusivement au moment de la compilation en fonction du type de la pointeur.


Mon problème, ou comment j'en suis venu ici

Je suis d'essayer d'utiliser quelque chose comme cela maintenant pour un cubefile de la classe de base avec basé sur un modèle optimisé charge des fonctions qui seront mis en œuvre différemment pour les différents types de cubes (certains stockées par pixel, par l'image, etc).

Un peu de code:

virtual void  LoadCube(UtpBipCube<float> &Cube,long LowerLeftRow=0,long LowerLeftColumn=0,
        long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;
virtual void  LoadCube(UtpBipCube<short> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
        long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;
virtual void  LoadCube(UtpBipCube<unsigned short> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
        long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;

Ce que j'aimerais qu'il soit, mais il ne compile pas en raison d'un virtuel basé sur un modèle de liste déroulante:

template<class T>
    virtual void  LoadCube(UtpBipCube<T> &Cube,long LowerLeftRow=0,long LowerLeftColumn=0,
            long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;

J'ai fini par déménager le modèle de déclaration au niveau de la classe. Cette solution aurait forcé les programmes à savoir à propos des types de données spécifiques, ils auraient lu avant de lire, ce qui est inacceptable.

Solution

attention, ce n'est pas très jolie, mais elle m'a permis de supprimer répétitif exécution de code

1) dans la classe de base

virtual void  LoadCube(UtpBipCube<float> &Cube,long LowerLeftRow=0,long LowerLeftColumn=0,
            long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;
virtual void  LoadCube(UtpBipCube<short> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
            long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;
virtual void  LoadCube(UtpBipCube<unsigned short> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
            long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1) = 0;

2) et dans les classes d'enfants

void  LoadCube(UtpBipCube<float> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
        long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1)
{ LoadAnyCube(Cube,LowerLeftRow,LowerLeftColumn,UpperRightRow,UpperRightColumn,LowerBand,UpperBand); }

void  LoadCube(UtpBipCube<short> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
        long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1)
{ LoadAnyCube(Cube,LowerLeftRow,LowerLeftColumn,UpperRightRow,UpperRightColumn,LowerBand,UpperBand); }

void  LoadCube(UtpBipCube<unsigned short> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
        long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1)
{ LoadAnyCube(Cube,LowerLeftRow,LowerLeftColumn,UpperRightRow,UpperRightColumn,LowerBand,UpperBand); }

template<class T>
void  LoadAnyCube(UtpBipCube<T> &Cube, long LowerLeftRow=0,long LowerLeftColumn=0,
        long UpperRightRow=-1,long UpperRightColumn=-1,long LowerBand=0,long UpperBand=-1);

Notez que LoadAnyCube n'est pas déclarée dans la classe de base.


Voici un autre débordement de pile de répondre avec un travail autour de: besoin d'un modèle virtuel membre de la solution de contournement.

19voto

Brent81 Points 400

Le code suivant peut être compilé et s'exécute correctement, en utilisant MinGW G++ 3.4.5 sur windows 7:

#include <iostream>
#include <string>

using namespace std;

template <typename T>
class A{
public:
    virtual void func1(const T& p)
    {
        cout<<"A:"<<p<<endl;
    }
};

template <typename T>
class B
: public A<T>
{
public:
    virtual void func1(const T& p)
    {
        cout<<"A<--B:"<<p<<endl;
    }
};

int main(int argc, char** argv)
{
    A<string> a;
    B<int> b;
    B<string> c;

    A<string>* p = &a;
    p->func1("A<string> a");
    p = dynamic_cast<A<string>*>(&c);
    p->func1("B<string> c");
    B<int>* q = &b;
    q->func1(3);
}

et la sortie est:

A:A<string> a
A<--B:B<string> c
A<--B:3

Et plus tard, j'ai ajouté une nouvelle classe X:

class X
{
public:
    template <typename T>
    virtual void func2(const T& p)
    {
        cout<<"C:"<<p<<endl;
    }
};

Quand j'ai essayé d'utiliser la classe de X dans main() comme ceci:

X x;
x.func2<string>("X x");

g++ rapport d'erreur suivants:

vtempl.cpp:34: error: invalid use of `virtual' in template declaration of `virtu
al void X::func2(const T&)'

Il est donc évident que:

  • fonction membre virtuelle peut être utilisée dans un modèle de classe. Il est facile pour le compilateur de construire vtable
  • Il est impossible de définir un modèle de classe de la fonction membre virtuelle, comme vous pouvez le voir, il est difficile de déterminer la fonction de signature et d'allouer vtable des entrées.

4 votes

C'est totalement différent de ce que la question demandait. Ici, la classe de base entière est modélisée. J'ai déjà compilé ce genre de choses. Cela compilerait aussi sur Visual Studio 2010

6voto

mag Points 3381

Pensez-y pendant un moment :

Q.1. Comment faire une fonction virtuelle modélisée ?
Q.2. Quelle est sa signature ?
Q.3. Combien d'entrées de vtable réservez-vous ?
Q.4 : Comment faire la différence entre un override/hide et une overload ?

J'espère que cela vous aidera.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X