1
votes

Alternatives de fonction virtuelle modèles

Quelles sont les alternatives aux fonctions virtuelles basées sur des modèles?

Voici une abstraction de mon cas d'installation:

int main( )
{
    std::vector< CParent * > vecAbstractPointers;

    CChild1 child1;
    vecAbstractPointers.emplace_back( reinterpret_cast< CParent * >( &child1 ) );
    CChild2 child2;
    vecAbstractPointers.emplace_back( reinterpret_cast< CParent * >( &child2 ) );

    *vecAbstractPointers[ 0 ] /*char( child1 )*/ = 'b';
    *vecAbstractPointers[ 1 ] /*int( child2 )*/ = 8;
}

Voici une abstraction de mon cas d'utilisation: p>

class CParent
{
    bool m_bExampleParentData = true;

public:
    template< typename t > virtual operator t( ) = 0;
};

class CChild1: public CParent
{
    char m_chExampleChild1Data = 'c';

public:
    operator char & ( ) override
    {
        return m_chExampleChild1Data;
    }
};

class CChild2: public CParent
{
    int m_iExampleChild2Data = 17;

public:
    operator int & ( ) override
    {
        return m_iExampleChild2Data;
    }
};

Je suis sûr qu'il y a quelque chose de stupidement évident et de beaucoup plus simple que ce code, mais je ne peux pas y penser pour la vie de moi.

Nous espérons que cela permettra d'affecter des membres des classes enfants via une fonction virtuelle dans la classe parent sans connaître les types des membres de la classe enfants. Je suppose que si je ne peux rien faire moi-même, je pourrai peut-être implémenter quelque chose en utilisant le standard (comme std :: variant ou std :: any ) mais J'aimerais éviter cela. J'essaie de garder le code de fin aussi absent que possible du casting (donc le casting implicite).


5 commentaires

Cela n'a pas beaucoup de sens. Acceptons pour des raisons d'argumentation que vous avez réussi à définir vos classes comme vous le souhaitez. Comment un utilisateur de votre bibliothèque, n'ayant que quelques pointeurs CParent * en main, saurait-il attribuer un char à certains et un int aux autres? L'intérêt d'une classe de base polymorphe est qu'elle présente une interface uniforme.


Pourriez-vous montrer un exemple qui représente réellement ce que vous essayez d'accomplir? Cela ressemble beaucoup à un problème XY


Encore une fois, comment le code qui n'a qu'un tas de références CParent & en main, saurait-il attribuer char à certains d'entre eux mais int aux autres? Si vous avez ces informations d'une manière ou d'une autre, vous pouvez simplement convertir en CChild1 & ou CChild2 & (à ce stade, on ne sait pas à quoi sert CParent ).


Notez que = ne prend pas en compte les conversions définies par l'utilisateur pour son opérande de gauche, et que vous n'avez pas besoin (et ne devez donc pas utiliser) reinterpret_cast ici.


Ne faites pas plus de travail pour les autres en vandalisant vos messages. En publiant sur le réseau Stack Exchange (SE), vous avez accordé un droit non révocable, sous un CC Licence BY-SA , pour que SE distribue le contenu (c'est-à-dire quels que soient vos choix futurs). Par politique SE, la version non vandalisée est distribuée. Ainsi, tout vandalisme sera annulé. Veuillez consulter: Comment fonctionne la suppression? … . Si la suppression est autorisée, il y a un bouton "Supprimer" sous le message, sur la gauche, mais ce n'est que dans les navigateurs, pas dans l'application mobile.


3 Réponses :


1
votes

affecter les membres des classes enfants via une fonction virtuelle dans la classe parent sans connaître les types des membres de la classe enfants

Ce n'est généralement pas possible en C ++. C ++ est un langage typé statiquement. En tant que tel, le type de chaque expression doit être connu au moment de la compilation. Les modèles suivent également cette règle; ils permettent simplement la génération de fonctions / classes / etc en fonction de types particuliers. Donc, avec une fonction modèle qui prend un paramètre de type T , il y a un nombre infini de fonctions possibles qui pourraient être générées. Le compilateur ne génère que ceux qui sont utilisés statiquement.

C'est du polymorphisme statique.

Cette génération de modèle de fonctions est également la raison pour laquelle les fonctions de modèle virtuel ne sont pas autorisées. Par définition, il devrait y en avoir un nombre infini de versions, et une classe dérivée devrait les implémenter. Et le mécanisme virtuel repose généralement sur l'identité d'une fonction, qui n'existe pas vraiment pour un modèle.

Les fonctions

virtuelles expriment un polymorphisme dynamique / d'exécution. Ceux-ci sont basés sur des prototypes définis dans des classes de base, qui sont implémentés dans des classes dérivées. Mais ces prototypes ne sont que des fonctions régulières . Fonctions qui ont une signature de fonction complète, y compris leur type de retour. Une signature qui doit suivre les règles du C ++: les types sont connus à la compilation.

En tant que tel, la seule façon d'implémenter ce dont vous parlez est de conditionner la valeur de manière à ce que le compilateur ne sache pas quel est le type, mais la source et la destination le font. std :: any est un outil décent pour cela.


3 commentaires

"les fonctions de modèle ne sont pas autorisées. Par définition, il devrait y en avoir un nombre infini de versions" est-ce parce qu'elles doivent être connues à l'avance, c'est-à-dire lors de la compilation, bien avant la temps de liaison où un autre module essaierait d'utiliser une telle classe? et donc si un compilateur devait être présent au moment de la liaison (ou même de l'exécution), il serait alors capable de générer et de compiler n'importe quel type spécifique d'une telle fonction selon les besoins, à un site d'appel réel? (Je comprends que ce ne serait pas un (aujourd'hui?) C ++ alors, j'apprécierais juste des commentaires pour voir si j'ai bien compris).


@WillNess: " donc si un compilateur devait être présent " Non, vous auriez potentiellement besoin de recompiler l'application entière . Rappelez-vous: la plupart des implémentations de fonctions virtuelles utilisent des vtables, qui sont des structures contenant des pointeurs de fonction. Un nombre spécifique de pointeurs de fonction. Chaque fois qu'une nouvelle instanciation était produite, un nouveau pointeur de fonction devrait apparaître comme par magie. Ce qui devrait changer une vtable qui est utilisée par des objets. Même P1609 garantit que JIT-C ++ veille à éviter les choses qui modifient le code existant .


Merci pour la réponse. ressemble à un problème de mise en œuvre? peut-être que les vtables pourraient être implémentées différemment, ou tout le mécanisme de surcharge ... ouais, ce serait probablement encore plus éloigné du C ++ tel qu'il est. Je pensais que cela remplacerait (et non ajouter ) l'un des pointeurs par une version spécifique pour tel ou tel type d'instanciation spécifique. mais cela fait trop longtemps que j'ai écrit C ++ et ma connaissance de ces choses est fragmentaire. Merci!



1
votes

Voici ce que j'ai proposé qui nécessite une modification minimale de votre code publié.

#include <iostream>
#include <vector>

// Base class to be able to hold pointers to derived class instances.
class CParent
{
   bool m_bExampleParentData = true;
   public:

      // Add this to make sure deleting object through base class pointers works
      // as well as dynamic_cast is supported.
      virtual ~CParent() {}
};

// Base class to allow inheritance of specific conversion operators.
template <typename T>
class TParent
{
   public:
      virtual operator T() = 0;
};

// By inheriting from TParent<char&>, this class must support conversion
// to char& to allow instantation of class objects.
class CChild1: public CParent, public TParent<char&>
{
    char m_chExampleChild1Data = 'c';

    operator char & ( ) override
    {
        return m_chExampleChild1Data;
    }
};

// By inheriting from TParent<int&>, this class must support conversion
// to int& to allow instantation of class objects.
class CChild2: public CParent, public TParent<int&>
{
    int m_iExampleChild2Data = 17;

    operator int & ( ) override
    {
        return m_iExampleChild2Data;
    }
};

int main()
{
    std::vector< CParent * > vecAbstractPointers;

    CChild1 child1;
    vecAbstractPointers.emplace_back( reinterpret_cast< CParent * >( &child1 ) );
    CChild2 child2;
    vecAbstractPointers.emplace_back( reinterpret_cast< CParent * >( &child2 ) );

    // Rather ugly casts to allow assignment of 'b' and 8.
    static_cast<char&>(*(dynamic_cast<TParent<char&>*>(vecAbstractPointers[0]))) = 'b';
    static_cast<int&>(*(dynamic_cast<TParent<int&>*>(vecAbstractPointers[1]))) = 8;

    // Rather ugly casts to allow extraction of 'b' and 8.
    std::cout << static_cast<char&>(*(dynamic_cast<TParent<char&>*>(vecAbstractPointers[0]))) << std::endl;
    std::cout << static_cast<int&>(*(dynamic_cast<TParent<int&>*>(vecAbstractPointers[1]))) << std::endl;
}

PS Veuillez ne pas utiliser ce code dans des applications réelles. Je suis sûr qu'il existe une meilleure conception pour ce que vous essayez d'accomplir.


0 commentaires

0
votes
std::vector< CParent * > vecAbstractPointers;

CChild1 child1;
vecAbstractPointers.emplace_back( &child1 );
CChild2 child2;
vecAbstractPointers.emplace_back( &child2 );

*vecAbstractPointers[ 0 ] /*char( child1 )*/ = 'b';
*vecAbstractPointers[ 1 ] /*int( child2 )*/ = 8;

0 commentaires