IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage C++ Discussion :

[Templates] templates vs. directives de préprocesseurs


Sujet :

Langage C++

  1. #1
    Membre régulier

    Inscrit en
    Juin 2008
    Messages
    49
    Détails du profil
    Informations forums :
    Inscription : Juin 2008
    Messages : 49
    Points : 114
    Points
    114
    Par défaut [Templates] templates vs. directives de préprocesseurs
    Bonjour,

    Je suis actuellement en stage et je travaille sur un code existant de simulation numérique. Le but est d'améliorer ce code en le rendant plus rapide. Les autres développeurs n'ont pas de grandes connaissances en programmation et surtout pas en C++.

    Le programme possède plusieurs solveurs d'équations mais seulement un de ces solveurs est utilisé lors d'une simulation. Le code actuel utilise un test pour vérifier avant chaque appel du solveur, lequel doit être lancé. Le choix du solveur se faisant dans un fichier de configuration lu au démarrage du programme. Le solveur étant utilisés des millions de fois par secondes, cette solution est loin d'être optimale.

    J'ai donc proposé d'utiliser des templates pour remplacer ces choix de paramètres. Lors de la compilation, l'optimiseur pourra ainsi éliminer les branches inutiles et supprimer ces tests. Au total, il y a une dizaine de paramètres qui pourraient être modifiés de la sorte.

    Un de mes collégues me fait alors remarquer que je pourrais faire la même chose en utilisant des directives de pré-processeur. Ce qui est correct.

    J'ai relevé les inconvénients suivants aux directives de pré-processeurs :
    1. Difficulté de lecture du code.
    2. Possibles effets de bord.
    3. Pas de typage des options.


    Et les inconvénients suivants aux templates :
    1. Compilation pouvant devenir très longue.
    2. Présence uniquement de fichiers "header".
    3. Impossibilité de créer des "mini-bibliothèques" pour chaque module du projet.


    Y-a-t'il quelque chose que je devrais ajouter à ces listes ?
    Quel est selon vous la meilleure solution ?

    Merci d'avance pour vos réponses.

  2. #2
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 612
    Points
    30 612
    Par défaut
    Salut,

    A mon sens, tu gagnera énormément en modularité et en souplesse avec les template, et principalement avec les traits de politiques et les politiques .

    De plus, tu t'évitera sans doute bien des recopies manuelles de code, et tu limitera de ce fait les risques d'erreur

    En effet, l'utilisation des directives préprocesseurs devrait être, à mon sens, limitée à quelques points particuliers:
    1. l'inclusion des fichiers d'en-tête ( directive #include )
    2. les garde anti inclusion (directives #ifndef MACHINCHOSE_H #define #endif) qui entourent le contenu de l'en-tête
    3. Le choix de l'exportation ou non et de l'importation ou non (lorsque l'on envisage de créer éventuellement une dll)
    4. Quelques cas permettant de simplifier l'écriture

    Le dernier point serait celui qui permettrait de remplacer un code proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    typedef TypeList<int,<TypeList<char,TypeList<double,NullType> > > malist;
    en quelque chose proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    typedef TYPELIST_3(int, char, double) malist;
    (exemple personnel basé sur la bibliothèque Loki)

    En effet, imagine, simplement, que tu doive avoir recours dans un code à deux options a priori incompatibles entre elles...

    Si tu décide d'utiliser les directives préprocesseurs, tu devra, dans le meilleur des cas, créer deux fichiers d'implémentation, dans le pire, veiller à "dé définir" chaque symbole défini et à "ré-inclure" le fichier d'en-tête. avant de recopier quasi mot à mot le code dont tu as besoin, sous une forme proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    #define VALEUR1 true
    #define VALEUR2 false
    /*...
     * Macros.h se base sur la définition de VALEUR1 et de VALEUR2
     * pour définir d'autres macros ;)
     */
    #include "Macros.h"
    /* le code adapté à VALEUR1 et VALEUR2
     * et utilisant ce qui est défini dans Macros.h
     */
    #undef VALEUR1
    #undef VALEUR2
    /* pour permettre la ré inclusion de Macros.h
     */
    #undef MACROS_H
    /* il ne faudrait pas oublier de dé définir les différentes
     * marcros en question
     * ...
     */
    /* changeons les valeurs */
    #define VALEUR1 false
    #define VALEUR2 true
    /* ré inclusion de macros.h */
    #include "macros.h"
    /*et continuons notre code */
    Alors que si tu utilises correctement les traits de politiques et les politiques, tu peux en arriver à quelque chose proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    struct Politique1_vrai
    {
        void doSomething(){/*...*/}
    };
    struct Politique1_faux
    {
        void doSomething(){/*...*/}
    };
    struct Politique2_vrai
    {
        void doOtherThing(){/*...*/}
    };
    struct Politique2_faux
    {
        void doOtherThing(){/*...*/}
    };
    template <class Pol1, class Pol2>
    class Work:public Pol1, public Pol2
    {
        private:
            /* quelques données intéressantes */
    };
    /* Dans l'implémentation:
     * Je veux utiliser Politique1_vrai et politique2_vrai...
     */
    typedef Work<Politique1_vrai,politique2_vrai> WorkVraiVrai;
    WorkVraiVrai wvv;
    wvv.doSomenthing();
    wvv.doOtherThing();
     
    /* Je veux utiliser il existe 4 possibilités différentes, et nous pouvons 
     * chaque fois créer un type qui correspond par simple typedef (pour la
     * facilité d'écriture ;)) sans avoir à réécrire une ligne de code 
     * supplémentaire
     * 
     * Le mieux de tout, c'est que nous n'aurons pas de problème
     * avec les différentes options incompatibles
     */
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  3. #3
    Expert éminent

    Inscrit en
    Novembre 2005
    Messages
    5 145
    Détails du profil
    Informations forums :
    Inscription : Novembre 2005
    Messages : 5 145
    Points : 6 911
    Points
    6 911
    Par défaut
    Citation Envoyé par Nanoc Voir le message
    Le programme possède plusieurs solveurs d'équations mais seulement un de ces solveurs est utilisé lors d'une simulation. Le code actuel utilise un test pour vérifier avant chaque appel du solveur, lequel doit être lancé. Le choix du solveur se faisant dans un fichier de configuration lu au démarrage du programme. Le solveur étant utilisés des millions de fois par secondes, cette solution est loin d'être optimale.
    Un test par rapport à un solveur d'équations? Est-ce qu'il prend réellement une partie significative du temps d'exécution? Si tu n'as pas fait de mesures, c'est à faire avant de chercher des méthodes compliquées pour un gain qui me semble loin d'être certain.

    Il y a vraisemblablement pas mal de choses à faire avant de chercher à optimiser au prix d'utilisation d'exécutables différents.
    Les MP ne sont pas là pour les questions techniques, les forums sont là pour ça.

  4. #4
    Membre régulier

    Inscrit en
    Juin 2008
    Messages
    49
    Détails du profil
    Informations forums :
    Inscription : Juin 2008
    Messages : 49
    Points : 114
    Points
    114
    Par défaut
    @koala01:
    L'idée est plutot la suivante:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    for all body 
        for all other bodies
           //Plein de trucs
           if choixSolveur == 1
                solveur1()
           else
                solveur2()
          //Encore plein de trucs
        endfor
    endfor
    Toutes les options sont clairement mutuellement exclusives et ça ne devrait pas bouger. L'utilisation des classes de politique est intéressante mais rendent le code et les erreurs de compilation vraiment complexes. De plus, le code complet faisant plusieurs milliers de lignes, si tout se retrouve dans une seule unité de traduction, j'ai peur que le compilateur ne rende l'âme. Bien que je n'aie aucune idée de l'existence ou non d'une telle limite.


    @Jean-Marc.Bourguet:
    Des tests ont été effectués (en commentant le code qui ne sera jamais appelé) et le gain est important.
    C'est un code scientifique. Les codes similaires que nous avons utilisés sont tous à recompiler avant chaque lancement. Ce n'est certainement pas pour rien.

  5. #5
    Expert éminent sénior
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 612
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 52
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 612
    Points : 30 612
    Points
    30 612
    Par défaut
    Mais il faudrait que tu précise la manière dont ce que tu présente sous le nom de choixSolveur est calculé (avant d'être testé pour choisir le solveur qui t'intéresse) ainsi que du nombre d'options à prendre en compte (et pas forcément la valeur de ces options)...

    Car, si tu as, mettons, dix options qui autorisent toutes ne serait-ce que 2 valeur (vrai et faux), la combinaison de toutes ces options te fournit une bonne centaines d'optiques différentes.

    Et je présume que, si tu observe un comportement donné avec une des options ayant pour valeur "vrai", tu observera un comportement identique chaque fois que cette option aura la valeur "vrai", et ce, quelle que soit la valeur des autres options...

    L'idée est donc de créer des traits de politiques représentant les comportement observables pour les différentes valeurs pour une option donnée, et de regrouper les différents traits de politiques en vue d'obtenir le comportement d'un solveur précis correspondant à l'ensemble des politiques que l'on a décidé.

    Au final, chaque comportement (dépendant d'un option particulière) étant défini, tu n'aura plus qu'à... appeler les différents comportements dans l'ordre que tu souhaite pour obtenir n'importe quel rélutat complexe.

    Et ton code qui, à l'heure actuelle, représente plusieurs milliers de lignes subira une cure d'amaigrissement draconnienne parce que tu évitera les copies de code inutiles

    De plus, l'avantage des template est que le code exécutable au niveau du processeur permettant de les gérer n'est réellement créé que lorsqu'il est utilisé et que donc tu obtiens un code exécutable qui ne correspond qu'aux valeurs sélectionnées pour tes différentes options

    En outre, rien ne t'interdit de séparer les différents traits de politiques dans différents fichiers (ce qui, je l'accorde ne change pas grand chose au final, du fait des inclusions qui en découlent).

    Enfin, il ne faut pas trop t'inquiéter de la capacité du compilateur à gérer le tout: les valeurs minimales admises par les compilateurs qui respectent la norme sont:
    • 256 niveaux de boucles et de tests imbriqués
    • 256 niveaux de parenthèses imbriqués dans une expression complète
    • 65 536 identifiants externes utilisés par unité de compilation
    • 256 paramètres déclarés lors de la déclaration d'une fonction
    • 256 arguments passés lors de l'appel d'une fonction
    • 16 384 données membres au sein d'une classe unique
    • 16 384 classes de base pour une classe unique
    • 1 024 classes de base directes pour une classe unique
    • 1 024 arguments template dans la déclaration d'un template
    • 65 536 caractères dans une ligne de code source logique
    (il y a d'autres valeurs que je n'ai pas recopiées )

    Bref, autant te dire que, avant de saturer le compilateur, tu as vraiment de quoi faire
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  6. #6
    Expert confirmé
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Décembre 2003
    Messages
    3 549
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Décembre 2003
    Messages : 3 549
    Points : 4 625
    Points
    4 625
    Par défaut
    Le préprocesseur c'est bien pour la génération de code, les templates c'est bien pour paramétriser du code.
    L'exemple de typelists de Loki plus haut n'est pas forcément bon, étant donné que le système de Loki est has been et qu'on fait bien mieux sans préprocesseur pour l'utilisateur.
    Boost ftw

  7. #7
    Alp
    Alp est déconnecté
    Expert éminent sénior

    Avatar de Alp
    Homme Profil pro
    Inscrit en
    Juin 2005
    Messages
    8 575
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations forums :
    Inscription : Juin 2005
    Messages : 8 575
    Points : 11 860
    Points
    11 860
    Par défaut
    Citation Envoyé par loufoque Voir le message
    Le préprocesseur c'est bien pour la génération de code, les templates c'est bien pour paramétriser du code.
    L'exemple de typelists de Loki plus haut n'est pas forcément bon, étant donné que le système de Loki est has been et qu'on fait bien mieux sans préprocesseur pour l'utilisateur.
    L'état de l'art à ce niveau, ça se voit plutôt dans Boost oui.

    Je conseille en effet d'utiliser des templates pour paramétrer et sélectionner à la compilation le meilleur solveur -- si tant est que c'est "calculable" à la compilation.

  8. #8
    Rédacteur

    Avatar de Matthieu Brucher
    Profil pro
    Développeur HPC
    Inscrit en
    Juillet 2005
    Messages
    9 810
    Détails du profil
    Informations personnelles :
    Âge : 42
    Localisation : France, Pyrénées Atlantiques (Aquitaine)

    Informations professionnelles :
    Activité : Développeur HPC
    Secteur : Industrie

    Informations forums :
    Inscription : Juillet 2005
    Messages : 9 810
    Points : 20 970
    Points
    20 970
    Par défaut
    Citation Envoyé par Nanoc Voir le message
    Toutes les options sont clairement mutuellement exclusives et ça ne devrait pas bouger. L'utilisation des classes de politique est intéressante mais rendent le code et les erreurs de compilation vraiment complexes. De plus, le code complet faisant plusieurs milliers de lignes, si tout se retrouve dans une seule unité de traduction, j'ai peur que le compilateur ne rende l'âme. Bien que je n'aie aucune idée de l'existence ou non d'une telle limite.
    L'objectif n'est pas d'avoir une seule unité de compilation (effectivement, le compilateur peut ne pas autant optimiser dans ce cas). L'objectif est de simplifier le code d'appel en terme de maintenance. Une fois qu'on est habitué aux erreurs template, c'est simple à comprendre.

    Ensuite, selon ton problème réel, il faut :
    - voir ce qui cloche : ce sont les tests qui sont embêtants ?
    - si ce sont les tests, le template ne va rien changer car le temps de recherche sera toujours en O(N) où N est le nombre de solveurs. Tu peux passer par une map pour passer en O(log(N))
    - si c'est le fait d'appeler souvent le dispatch, peut-être aussi que tu peux utiliser un pointeur de fonction pour ne faire la recherche qu'une seule fois.

    Dans ce cas, macro ou template, ça sera le même coût. La différence, c'est ce que tu mets derrière. Mais évidemment, si tu fais du C++, le dispatch, tu le fais soit en template, soit avec des maps (si c'est de la sélection, je tends à favoriser cette dernière option, elle est plus modulable à l'exécution au cas où tu ajouterais des solveurs à la volée sous forme de plugins).

  9. #9
    Membre régulier

    Inscrit en
    Juin 2008
    Messages
    49
    Détails du profil
    Informations forums :
    Inscription : Juin 2008
    Messages : 49
    Points : 114
    Points
    114
    Par défaut
    Dans le pseudo-code que j'ai fourni précédemment, choixSolveur est lu dans un fichier d'initialisation et ne change jamais au cours de l'exécution. Et il n'y a aucun intérêt à changer en cours de route.

    Il y a d'autres paramètres de ce genre, ce qui porte le nombre total de variantes possibles à prêt d'un million. Il n'est donc clairement pas possible de toutes les compiler et de lancer uniquement la bonne au démarrage. L'idée est donc de faire éliminer les branches inutiles au compilateur.

    Merci pour les chiffres de compilateur. Ca devrait pas poser de problèmes.

  10. #10
    Alp
    Alp est déconnecté
    Expert éminent sénior

    Avatar de Alp
    Homme Profil pro
    Inscrit en
    Juin 2005
    Messages
    8 575
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations forums :
    Inscription : Juin 2005
    Messages : 8 575
    Points : 11 860
    Points
    11 860
    Par défaut
    Le problème c'est que si tu lis le choix à l'exécution, tu ne peux rien faire à la compilation, puisque tu ne sais rien encore -- à moins de faire un "méta-compilateur" qui va voir dans le fichier et modifie le code en conséquence, puis lance le compilateur

  11. #11
    Membre régulier

    Inscrit en
    Juin 2008
    Messages
    49
    Détails du profil
    Informations forums :
    Inscription : Juin 2008
    Messages : 49
    Points : 114
    Points
    114
    Par défaut
    C'est bien pour cela que nous allons recompiler à chaque fois en sépifiant les options lors de la compilation. Après dans le code, cela peut-être implémenté sous forme de templates ou de directives de préprocesseurs. D'où mon questionnement.

  12. #12
    Alp
    Alp est déconnecté
    Expert éminent sénior

    Avatar de Alp
    Homme Profil pro
    Inscrit en
    Juin 2005
    Messages
    8 575
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Bouches du Rhône (Provence Alpes Côte d'Azur)

    Informations forums :
    Inscription : Juin 2005
    Messages : 8 575
    Points : 11 860
    Points
    11 860
    Par défaut
    J'verrais bien un code analogue à ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    template <class Computation>
    void DoCompute() 
    { 
      std::time_t start = std::time(NULL);
      Computation::compute();
      std::time_t end = std::time(NULL);
      std::cout << end - start << " seconds." << std::endl;
    }
     
    struct SingleThreadedComputation
    {
      static void compute()
      { 
        // implémentation mono-thread
      }
    };
     
    struct MultiThreadedComputation
    {
      static void compute()
      { 
        // implémentation multi-thread
      }
    };
     
    // par exemple :
    #ifdef STCOMPUTATION
    DoCompute<SingleThreadedComputation>();
    #elif defined MTCOMPUTATION
    DoCompute<MultiThreadedComputation>();
    #endif
     
    // comportement que l'on peut choisir soit avec un #define, 
    // soit avec l'option de compilation -DSTCOMPUTATION ou -DMTCOMPUTATION

  13. #13
    Inactif  
    Avatar de Mac LAK
    Profil pro
    Inscrit en
    Octobre 2004
    Messages
    3 893
    Détails du profil
    Informations personnelles :
    Âge : 49
    Localisation : France, Haute Garonne (Midi Pyrénées)

    Informations forums :
    Inscription : Octobre 2004
    Messages : 3 893
    Points : 4 846
    Points
    4 846
    Par défaut
    Si le problème est de connecter à l'exécution un solveur, tout en restant optimisé au maximum, pourquoi ne pas tout simplement utiliser des pointeurs de fonction, du moins si les solveurs utilisent les mêmes types de paramètres ?





    Sinon, côté macros, tu peux faire des macros d'activation / désactivation, par exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    #define USE_SOLVER_TRUC    0
    #define USE_SOLVER_MACHIN  1
     
    ...
     
    #define SOLVER_TRUC_BEGIN if (USE_SOLVER_TRUC) {
    #define SOLVER_TRUC_END }
     
    #define SOLVER_MACHIN_BEGIN if (USE_SOLVER_MACHIN) {
    #define SOLVER_MACHIN_END }
     
    ...
     
    SOLVER_TRUC_BEGIN
      Result = SolverTruc(Truc_Parameters);
    SOLVER_TRUC_END
     
    SOLVER_MACHIN_BEGIN
      Result = SolverMachin(Machin_Parameters);
    SOLVER_MACHIN_END
    C'est pas super beau, mais ça fonctionne très bien dès que l'on active les optimisations (qui vont supprimer le code mort). On peut aussi remplacer les macros "BEGIN"/"END" par des "#if (USE_SOLVER_XXX==1)"/"#endif", mais c'est un peu plus pénible à lire je trouve. Dans tous les cas, la plupart des définitions de macros peuvent être générées via un script, et mises dans un ou deux entêtes faciles à modifier.

    La dernière méthode :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    #define SOLVER_FUNC SolverMachin
    #define SOLVER_PARAM ParamMachin1, ParamMachin2, ParamMachin3
     
    ....
     
     
       Result = SOLVERFUNC(SOLVER_PARAM);
    Toutefois, cette manière de procéder est très proche des templates, tout en étant nettement plus pénible et moins souple à mettre en œuvre... Ce serait la seule solution en C, mais vu que tu es en C++, autant aller vers les templates si tu en arrives à ce stade.
    Mac LAK.
    ___________________________________________________
    Ne prenez pas la vie trop au sérieux, de toutes façons, vous n'en sortirez pas vivant.

    Sources et composants Delphi sur mon site, L'antre du Lak.
    Pas de question technique par MP : posez-la dans un nouveau sujet, sur le forum adéquat.

    Rejoignez-nous sur : Serveur de fichiers [NAS] Le Tableau de bord projets Le groupe de travail ICMO

  14. #14
    Membre régulier

    Inscrit en
    Juin 2008
    Messages
    49
    Détails du profil
    Informations forums :
    Inscription : Juin 2008
    Messages : 49
    Points : 114
    Points
    114
    Par défaut
    On va finalement partir vers quelque chose comme Alp propose.

    Les pointeurs de fonctions coutent plus chers à l'appel et les fonctions ne sont pas inlignable donc ça vaut pas vraiment la peine.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Problème avec template template template
    Par oodini dans le forum Langage
    Réponses: 6
    Dernier message: 23/11/2012, 14h40
  2. Template, template, encore des template
    Par olivier1978 dans le forum Langage
    Réponses: 1
    Dernier message: 16/11/2007, 20h33
  3. [Template] Template et Arbre
    Par GrooveRage dans le forum Langage
    Réponses: 4
    Dernier message: 18/05/2007, 11h28
  4. template > template >
    Par Nomade95000 dans le forum Langage
    Réponses: 6
    Dernier message: 10/05/2007, 18h21
  5. template<template<>>
    Par joker34 dans le forum C++
    Réponses: 7
    Dernier message: 25/01/2006, 09h45

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo