64 votes

Les modèles C++ sont-ils des macros déguisées ?

Je programme en C++ depuis quelques années, j'ai utilisé STL assez souvent et j'ai créé mes propres classes de modèles à plusieurs reprises pour voir comment cela se passait.

Aujourd'hui, j'essaie d'intégrer davantage les modèles dans ma conception OO, et une pensée tenace me revient sans cesse à l'esprit : Ce ne sont que des macros, en fait... Vous pourriez implémenter des auto_ptrs (plutôt UGLY) en utilisant des #defines, si vous le vouliez vraiment.

Cette façon de concevoir les modèles m'aide à comprendre comment mon code fonctionnera réellement, mais j'ai l'impression que je ne comprends pas ce que je veux dire. Les macros sont le mal incarné, mais la "métaprogrammation de modèles" fait fureur.

Alors, quelles sont les vraies distinctions ? et comment les modèles peuvent-ils éviter les dangers que #define vous fait courir, comme par exemple

  • Erreurs de compilation dans là où l'on ne s'y attend pas ?
  • Le gonflement du code ?
  • Difficultés de traçage du code ?
  • Définir les points d'arrêt du débogueur ?

1 votes

Pourquoi pensez-vous que la "métaprogrammation de modèles est à la mode" ?

0 votes

Non, ce n'est pas le cas :-) Et votre question est très tendue car les modèles n'aident à résoudre aucun des problèmes que vous mentionnez. Au contraire, ils aident à résoudre une toute autre catégorie de problèmes que vous ignorez commodément. La question présuppose la réponse, et si j'en avais le pouvoir, j'aurais l'intention de la disqualifier parce qu'elle est subjective et argumentée.

0 votes

@Omnifarious - s'agit-il d'un commentaire sur cette question, ou de celui qui a été fusionné avec le mien ? Cette agitation autour d'une question morte depuis un an me fait perdre la tête...

51voto

Ferruccio Points 51508

Les macros sont un mécanisme de substitution de texte.

Les modèles sont un langage fonctionnel complet qui est exécuté au moment de la compilation et qui est intégré dans le système de type C++. On peut les considérer comme un mécanisme d'extension du langage.

7 votes

Cela n'explique pas vraiment les différences pertinentes. "Les pommes ne sont-elles pas des oranges ?" -- Mauvaise réponse : "Non, les oranges sont des agrumes, mais les pommes sont produites par des plantes. Malus domestica ." -- Bonne réponse : "Non, les oranges sont orange et pleines de jus acide avec un peu de chair, tandis que les pommes sont rouges ou vertes et contiennent une chair beaucoup plus dense et une plus petite quantité de jus plus sucré.

0 votes

@user253751 Pour être honnête, il s'agit d'une question très vague à laquelle il est difficile de répondre correctement.

38voto

rlerallut Points 2806

Ils sont analysés par l'outil compilateur et non par un préprocesseur qui exécute avant le compilateur.

Voici ce que dit MSDN à ce sujet : http://msdn.microsoft.com/en-us/library/aa903548(VS.71).aspx

Voici quelques problèmes liés à la macro :

  • Le compilateur n'a aucun moyen de vérifier que les paramètres des macros sont de types compatibles.
  • La macro est développée sans vérification particulière du type.
  • Les paramètres i et j sont évalués deux fois. Par exemple, si l'un des paramètres a une variable postincrémentée, l'incrémentation est effectuée deux fois.
  • Les macros étant développées par le préprocesseur, les messages d'erreur du compilateur feront référence à la macro développée plutôt qu'à la définition de la macro elle-même. En outre, la macro apparaîtra sous sa forme développée lors du débogage.

Si cela ne vous suffit pas, je ne sais pas ce que c'est.

19 votes

Le lien MSDN utilise un modèle pour Min, qui est en quelque sorte le "mauvais exemple" par excellence. Voir l'article de Scott Meyer sur les modèles pour Min/Max. aristeia.com/Papers/C++ReportColumns/jan95.pdf

6 votes

Il est évident que vous avez techniquement raison, mais le fait de dire que l'un est traité par le préprocesseur et l'autre par le compilateur ne donne pas de raison pour laquelle l'un est meilleur que l'autre.

3 votes

@Roddy Vous êtes injuste. Min en tant que modèle est assez simple à comprendre dans son état imparfait et offre une meilleure protection que les macros. Alexandrescu a une solution pour le problème min/max, mais elle est assez complexe, trop complexe à mon goût.

34voto

Jeff B Points 1008

De nombreux commentaires tentent de différencier les macros et les modèles.

Oui, il s'agit de la même chose : des outils de génération de code.

Les macros sont une forme primitive, sans beaucoup d'application de la part du compilateur (comme faire des objets en C - c'est possible, mais ce n'est pas joli). Les modèles sont plus avancés, et disposent d'un meilleur contrôle de type du compilateur, de messages d'erreur, etc.

Cependant, chacun possède des atouts que l'autre n'a pas.

Les modèles ne peuvent générer que des types de classes dynamiques, alors que les macros peuvent générer presque n'importe quel code (autre qu'une autre définition de macro). Les macros peuvent être très utiles pour intégrer des tableaux statiques de données structurées dans votre code.

Les modèles, quant à eux, permettent d'accomplir des choses vraiment FUNKY qui ne sont pas possibles avec les macros. Par exemple :

template<int d,int t> class Unit
{
    double value;
public:
    Unit(double n)
    {
        value = n;
    }
    Unit<d,t> operator+(Unit<d,t> n)
    {
        return Unit<d,t>(value + n.value);
    }
    Unit<d,t> operator-(Unit<d,t> n)
    {
        return Unit<d,t>(value - n.value);
    }
    Unit<d,t> operator*(double n)
    {
        return Unit<d,t>(value * n);
    }
    Unit<d,t> operator/(double n)
    {
        return Unit<d,t>(value / n);
    }
    Unit<d+d2,t+t2> operator*(Unit<d2,t2> n)
    {
        return Unit<d+d2,t+t2>(value * n.value);
    }
    Unit<d-d2,t-t2> operator/(Unit<d2,t2> n)
    {
        return Unit<d-d2,t-t2>(value / n.value);
    }
    etc....
};

#define Distance Unit<1,0>
#define Time     Unit<0,1>
#define Second   Time(1.0)
#define Meter    Distance(1.0)

void foo()
{
   Distance moved1 = 5 * Meter;
   Distance moved2 = 10 * Meter;
   Time time1 = 10 * Second;
   Time time2 = 20 * Second;
   if ((moved1 / time1) == (moved2 / time2))
       printf("Same speed!");
}

Le modèle permet au compilateur de créer et d'utiliser dynamiquement, à la volée, des instances de modèle sûres du point de vue du type. Le compilateur effectue en fait le calcul des paramètres du modèle au moment de la compilation, en créant des classes distinctes si nécessaire pour chaque résultat unique. Il existe un type implicite Unit<1,-1> (distance / temps = vitesse) qui est créé et comparé dans le conditionnel, mais qui n'est jamais explicitement déclaré dans le code.

Apparemment, quelqu'un dans une université a défini un modèle de ce type avec plus de 40 paramètres (besoin d'une référence), chacun représentant un type d'unité de physique différent. Pensez à la sécurité de type de ce type de classe, juste pour vos chiffres.

1 votes

J'avais une idée de ce que vous essayiez de faire jusqu'à ce que j'arrive à "Unit<d+d2,t+t2>", où j'ai perdu le fil. Pouvez-vous expliquer ce que vous essayez de faire et quels sont les avantages par rapport à "typedef double Distance"/"typedef double Time", qui sembleraient donner le même résultat ?

3 votes

Déclarer deux variables : Distance d ; Temps t ; Si la distance et le temps sont tous deux des doubles, l'énoncé (d = t) et l'expression (d == t) sont tous deux valides. Le modèle empêche cela - en fournissant une sécurité de type pour les valeurs numériques.

1 votes

Ah ! Merci. Je n'aurais JAMAIS pu le déduire moi-même !

22voto

Gregory Pakosz Points 35546

La réponse est tellement longue que je ne peux pas tout résumer :

  • par exemple, les macros ne garantissent pas la sécurité des types, alors que les modèles de fonctions le font :
    Le compilateur n'a aucun moyen de vérifier que les paramètres de la macro sont de types compatibles -- au moment où le modèle de fonction est instancié, le compilateur sait également si int o float définir operator +
  • Les modèles ouvrent la porte à la métaprogrammation (en bref, évaluer les choses et prendre des décisions au moment de la compilation) :
    Au moment de la compilation, il est possible de savoir si un type est intégral ou à virgule flottante, s'il s'agit d'un pointeur ou d'une constante qualifiée, etc... voir les "traits de type" dans la prochaine version de c++0x .
  • les modèles de classe ont une spécialisation partielle
  • les modèles de fonctions ont une spécialisation complète explicite, dans votre exemple add<float>(5, 3); pourrait être mis en œuvre différemment de add<int>(5, 3); ce qui n'est pas possible avec les macros
  • les macros n'ont pas de portée
  • #define min(i, j) (((i) < (j)) ? (i) : (j)) - les i y j sont évalués deux fois. Par exemple, si l'un des paramètres a une variable postincrémentée, l'incrémentation est effectuée deux fois
  • les macros étant développées par le préprocesseur, les messages d'erreur du compilateur feront référence à la macro développée plutôt qu'à la définition de la macro elle-même. De plus, la macro apparaîtra sous sa forme développée lors du débogage
  • etc...

Note : Dans certains cas rares, j'ai préféré m'appuyer sur des macros variadiques car les modèles variadiques n'existent pas jusqu'à ce que le c++0x devienne courant. C++11 est en direct.

Références :

0 votes

Nous avons ici un "downvoter" en colère ;-) Je n'ai pas non plus la moindre idée de la raison pour laquelle j'ai été downvoté ;-)

0 votes

Il est possible qu'un autre participant vous mette des bâtons dans les roues en voulant que sa réponse arrive en tête de liste, où elle a plus de chances d'être acceptée.

0 votes

Parce qu'il pense que son incapacité à poser des questions est une raison suffisante pour rejeter la faute sur tous ceux qui n'ont pas donné la réponse qu'il souhaitait. Je suis presque tenté de downvoter la question, mais ce dont j'ai besoin, c'est d'un moyen de downvoter la personne ;-)

12voto

GManNickG Points 155079

Au niveau le plus élémentaire, oui, les modèles sont simplement des remplacements de macro. Mais vous faites l'impasse sur beaucoup des choses en y réfléchissant de cette manière.

Pensez à la spécialisation des modèles, qui, à ma connaissance, ne peut pas être simulée par des macros. Non seulement cela permet une implémentation spéciale pour certains types, mais c'est aussi l'un des éléments clés de la métaprogrammation des modèles :

template <typename T>
struct is_void
{
    static const bool value = false;
}

template <>
struct is_void<void>
{
    static const bool value = true;
}

Ce qui, en soi, n'est qu'un exemple de la beaucoup de choses que vous pouvez faire . Les modèles eux-mêmes sont Turing-complets.

Cela ne tient pas compte des éléments fondamentaux, tels que la portée, la sécurité des types et le fait que les macros sont plus compliquées.

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