7
votes

Opérateurs surchargés et héritage et modèles (une combinaison formidable)

Salutations Tout.

J'écris du code à l'aide de la bibliothèque d'unités de boost et j'ai rencontré un problème.

J'ai réussi à résumer le problème du code de boost afin que vous ne cherchiez pas à regarder à travers des ramed of Boost Modèle Meta Programming. Bien que je suis sûr que si vous avez une expérience avec cela, cela pourrait aider. Voici la reproduction: xxx

donc le problème (spécifiquement) est la suivante: myU * myBase utilise opérateur * (u, const BASE &) et renvoie le type de BASE , tous bien jusqu'à présent. Tandis que myu * myderived insiste sur l'utilisation de U :: opérateur * (Y) et renvoie donc un q , pas bon parce que je voulais un < code> base à nouveau.

maintenant, toutes les classes autres que base et dérivé sont des classes de bibliothèque de boost afin que je ne puisse pas modifier les membres de U. Comment puis-je "battre" u :: opérateur * (y) pour la déduction / instanciation de la surcharge / du modèle, dans ce cas, dans un élégant et "résolu une fois pour toujours".

J'utilise MSVC ++ 2008 au cas où il est pertinent pour quiconque.

Modifier: Ajout d'une solution possible (assez probable) dans les réponses


5 commentaires

+1 pour un code d'exemple autonome autonome. :)


Qu'est-ce qui vous est venu à l'esprit: Est-ce que le boost u classe utilise Sfinae sur l'opérateur?


Et vous venez d'apprendre que malheureusement l'héritage et le modèle ne se mélangent pas bien: /


@Matthieu: Ce n'est certainement pas vrai. Ils se mélangent très bien, car ils résolvent des problèmes orthogonaux. Les combinaisons de métaprogramming de compilation et le polymorphisme d'exécution peuvent construire un excellent code.


@Alexandre C: Je suis d'accord, vous pouvez résoudre un excellent problème, mais avoir des classes de base et des arguments de modèle dans différentes surcharges et les amener à agir comme vous le souhaitez peut conduire à beaucoup de gratter. Il existe bien sûr des solutions, mais certainement pas aussi immédiat que l'on pouvait penser, à juste titre à cause de l'orthogonalité des approches :) Je pense avoir résolu celui-ci sans trop de problèmes, mais toujours, cela a pris plusieurs jours pour personnaliser Sortir l'exemple minimal: /


6 Réponses :


2
votes

Premièrement, le problème: le paramètre const basé & de l'opérateur sera toujours un ajustement pire que le paramètre de modèle du paramètre de modèle en raison de la conversion de dérivé à base .
Ensuite, la solution: fournir un opérateur surchargé * pour chaque classe dérivée. : (


2 commentaires

@ Xeo: J'ai remarqué cela juste comme je l'ai posté, je l'ai changé en const Base & , ce qui est dans le code réel. Cela n'affecte pas le résultat. J'espère qu'il y a une meilleure solution que l'opérateur * pour chaque classe dérivée.


@Edf: J'appuie au moins connaître de non autre. Même en prenant le paramètre de base comme un pointeur n'a pas aidé, ce que je pensais pourrait ...: /



2
votes

L'utilisation des éléments suivants doit corriger votre problème xxx

au lieu de xxx

Ce n'est pas ce que nous pouvions appeler une "solution propre" bien que. J'essaie de trouver une meilleure façon de le faire.


4 commentaires

Le problème évident avec cette approche est la tranchage. Peut-être que @edf a besoin de certaines fonctions virtuelles appelées à travers la référence de base? : /


Cela pourrait le réparer. Cependant, les classes dérivées en question sont utilisées massivement et la coulée de chaque fois que chaque fois ne serait pas pratique pour le code de la clientèle (ce qui serait à peine conscient que le dérivé a même une classe de base, grâce à Typefefs).


On dit la vérité que la tranchée n'est pas vraiment un problème. Dérivé ne contient plus de données qu'une base, mais je n'ai jamais pensé que c'est une bonne idée de couper les choses même quand "ça ne pouvait pas faire mal".


@Valka: une distribution à base et (note la référence) corrige cependant les problèmes de tranchage. ;)



1
votes

Si vous avez le contrôle de la commande des opérateurs, vous pouvez définir votre opérateur * dans le sens opposé, c'est-à-dire xxx p> maintenant si vous avez essayé < / p> xxx

Il ne correspondrait pas au modèle de classe u , vous entourer du numéro de la fonction de modèle dans u primordial votre propre opérateur fonction.


5 commentaires

Le problème est que vous DO DO DO MYNERVIED * MYU et MYBASE * MYU et ne peut pas changer la commande. Je pense que le problème est que la surcharge fournie par u est toujours meilleure, car aucune réorganisation des paramètres n'est nécessaire.


Tous les opérateurs sont définis à la fois, dans mon code et la bibliothèque. J'ai laissé ces choses pour garder l'exemple aussi concis que possible, car il est déjà massif en ce qui concerne les problèmes de langage élémentaire.


Ok, eh bien, ça valait un coup de poignard si vous veniez de commencer, et n'avez pas eu de base de code étendue à maintenir :)


Apprécié, j'ai déjà passé des journées sur ce problème ... Ce n'est qu'aujourd'hui que j'ai finalement réussi à le reproduire sans les milles de modèle métaprogrammant que cela est intégré. Je me sente certainement plus proche de la solution que cela a été réduit à sa forme actuelle. Espérons que quelqu'un aura une idée de réflexion massive. Le problème est qu'il y a tant de façons de presque correctement.


@Edf: Ou réellement le réparer, mais avec une partie de maintenance massive de votre côté pour fournir tout l'opérateur surchargé * pour les classes dérivées. : /



-1
votes

Utilisation de sfinae pour C ++ 03 ( boost :: is_base_of est de boost.typetits): xxx

une solution alternative de le faire avec C ++ 0x ( std :: is_base_of est de ): xxx

Un test rapide indique que votre exemple semble travailler avec Sfinae, mais être averti que les deux opérateurs doivent être des surcharges pour que cela fonctionne: si opérateur * (Y) est le seul opérateur qui apparaît dans le jeu de résolution de surcharge (par exemple à cause de l'ADL ou parce que d'où opérateur * (u, base const &) est déclaré), Sfinae le fera disparaître (comme prévu) mais la résolution de la surcharge finira sans candidat.

comme Xeo a souligné, ce qui précède n'aide pas. Es-même que je vous rachene, voici une dernière possibilité:

dans l'exemple, le membre opérateur * (Y) est non-const. Si, cependant, il était constitué comme opérateur * (y) const comme bon style recommande, alors vous avez pourriez fournir un meilleur match non-const qui transmet à votre Opérateur * (u const &, base const &) . Ceci est fragile Toutefois: si votre code utilise un u const & , vous trébucherez sur l'erreur de compilation d'origine.


1 commentaires

L'OP a dit qu'il ne peut rien changer d'après la base et les cours dérivés, car cela vient de boost.



1
votes

problème intéressant. Les modèles et héritage ne se mélangent pas vraiment bien, et puisque vous ne pouvez pas corriger u code> cela fait un défi intéressant à coup sûr.

Je propose d'atténuer le mécanisme de déduction de la surcharge :) P> Le moyen le plus simple serait de fournir une surcharge pour chaque classe code> dérivée code>. Évidemment, il est impraticable. P>

Sauf si nous pouvions utiliser une classe d'assistance pour laquelle l'opérateur est écrit et mélanger Typefs de là pour le rendre transparent pour le client. P>

struct Base {};

template <typename T>
struct BaseT: Base
{
  typedef T Tag;
};

struct DerivedTag {};
typedef BaseT<DerivedTag> Derived;

class Q {};
class U
{
public:
  template< typename Y >
  Q operator * (Y)
  {
    Q r;
    return r;
  }
};

Base operator * (U, const Base &)
{
  Base r;
  return r;
}

template <typename T>
BaseT<T> operator*(U, BaseT<T> const&) { return BaseT<T>(); }

int main(int argc, char **argv)
{
  Base myBase;
  U myU;
  Base myOtherBase = myU * myBase;
  Derived myDerived;
  Derived myOtherDerived =  myU * myDerived;
  return 0;
}


0 commentaires

0
votes

Tout comme j'ai abandonné essayé de résoudre cette dernière nuit, la réponse m'a frappé comme une brique. Le résultat code> q code> de u :: opérateur * (base) code> est conceptuellement identique à la base base code> résultat tapé de opérateur * ( U, base) code>, même si elles sont représentées par différents types. Tout ce que j'ai besoin de faire est de fournir un constructeur pour base (const Q &) code> qui accepte le type Q.

class Base
{
public:
Base(){}
Base(const Q &){}
};


1 commentaires

Eh bien, cela résoudra-t-il réellement le problème? C'était en fait l'une de mes premières pensées, mais je pensais que vous ne pouviez pas faire de construction utile de ce q . Si cela fonctionne, alors félicitations. :)