Si je comprends bien ton premier code, dans le cas où je fais une instance avec void je n'aurais pas la variable data, c'est ça ?
Si je comprends bien ton premier code, dans le cas où je fais une instance avec void je n'aurais pas la variable data, c'est ça ?
Site perso
Recommandations pour débattre sainement
Références récurrentes :
The Cambridge Handbook of Expertise and Expert Performance
L’Art d’avoir toujours raison (ou ce qu'il faut éviter pour pas que je vous saute à la gorge {^_^})
C'est exactement ca (note bien que j'ai indiqué que ce code sert à rien, en général on se débrouille pour avoir les même identifiant accessible en public/protected). Ce qu'on change en général c'est ce qui est en private, l'implémentation des fonctions et les définitions des typedef/using
Par contre je ne connais pas le language D ni les autres concurrents du c++, donc, je ne vais m'étendre là dessus.
Le c++ a toujours été, idéal jusqu'à présent pour ce que je voulais créer. (Malgré ses défaults et des crashs parfois difficile à repérer là ou certains autres langages lanceraient une exception, lors des accès concurrents par exemple)
Ben si j'ai bien compris je trouve ça casse gueule : a priori, un template permet d'abstraire (e.g. faire une implémentation sur une base d'hypothèses réduite), et ce sont ses différentes extensions qui prendront en charge les hypothèses supplémentaires pour des contextes plus spécifiques. Là tu te donnes la possibilité de mélanger différents niveaux d'abstractions (le général et le spécifique) ce qui me semble plutôt crade. C'est comme :
Ça montre plus un modèle conceptuel foiré qu'autre chose pour moi. Après oui, plutôt que d'avoir un if que tu vérifies à chaque fois, tu as une implémentation qui est déjà spécifique sans le if, donc niveau perf c'est mieux, mais niveau modélisation ça me semble moisi.
Code Java : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10 public abstract class MyClass<T> { public void execute(T data) { // ... code générique if (data instanceof FileReader) { ((FileReader)data).close(); } // ... code générique } }
Site perso
Recommandations pour débattre sainement
Références récurrentes :
The Cambridge Handbook of Expertise and Expert Performance
L’Art d’avoir toujours raison (ou ce qu'il faut éviter pour pas que je vous saute à la gorge {^_^})
Je me demande notamment :
- qu'est-ce qui se passe si un autre réécrit la même spécialisation de template mais à sa sauce ? Si c'est pas faisable, ça montre bien que ce sont des définitions interdépendantes, et donc qui visent à rester ensemble, contrairement à des extensions. Faire la séparation n'apporte donc rien au niveau conceptuel, c'est que de la perf.
- qu'est-ce qui se passe si pour mon utilisation particulière, mon instance j'ai pas envie qu'elle ferme mes FileReader ? Si je suis obligé de surcharger la méthode en réimplmentant les parties génériques, ça montre bien que j'ai pris en comtpe trop d'hypothèses au niveau au niveau parent.
Site perso
Recommandations pour débattre sainement
Références récurrentes :
The Cambridge Handbook of Expertise and Expert Performance
L’Art d’avoir toujours raison (ou ce qu'il faut éviter pour pas que je vous saute à la gorge {^_^})
Site perso
Recommandations pour débattre sainement
Références récurrentes :
The Cambridge Handbook of Expertise and Expert Performance
L’Art d’avoir toujours raison (ou ce qu'il faut éviter pour pas que je vous saute à la gorge {^_^})
Si on a pas eu besoin de regarder à côté, c'est déjà un bon point
@Matthieu Vergne: J'ai donné cet exemple pour t'éclairer sur le fonctionnement. Sur le fond je suis partiellement d'accord avec toi. En réalité, personnellement, lorsque j'écris un template je ne le spécialise quasiment jamais, excepté dans un cas : spécialisé un trait. Par exemple pour déterminer si un type est un pointeur ou pas, dans ce cas la version générique a une donné statique false et on effectue une spécialisation partielle pour les types pointeurs où la donné statique sera à true. Ainsi à la compilation je peux écrire des fonctions qui auront des comportements différents dans les deux cas.
Par exemple pour les conteneurs, tous ne peuvent pas être parcouru de la même manière, ce qui influe sur la façon d'implémenter un algorithme. Dans ce cas on peut faire un classe template qu'il faut spécialiser lorsque l'on créé un conteneur pour qu'il indique le type de parcours possible, ensuite on peut écrire les différentes versions selon le type de parcours qui seront activés ou non selon le type de parcours.
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 struct parcours_1; struct parcours_2; template<class> struct parcours_type; template<class T> using parcours_t =typename parcours_type<T>::type; //Un algo template<class Range> enable_if_t<is_same<parcours_1,parcours_t<Range>>::value> algo_a(Range&&) { /*impl*/ } template<class Range> enable_if_t<is_same<parcours_2,parcours_t<Range>>::value> algo_a(Range&&) { /*impl*/ } //Un conteneur template<class> struct cont { /*impl*/ }; template<class T> struct parcours_type<cont<T>> { using type = parcours_1; };
Moui, ou peut-être que tu maîtrises suffisamment l'outil en question pour savoir l'adapter à chaque situation.
Et que prendre X heures pour coder tel ou telle fonctionnalité te semble tout à fait banal, alors qu'un autre langage utilisant un autre paradigme te permet de le faire en X minutes, pour des performances équivalentes.
http://www.traducteur-sms.com/ On ne sait jamais quand il va servir, donc il faut toujours le garder sous la main
Dans le cas où tu ne trouves aucune raison d'aller voir ailleurs, c'est que ce que tu as sous la main est parfait, non ? Après rien n'empêche que plusieurs solutions soient parfaites, mais tout au plus ça te fait un front de Pareto. Si t'as aucune raison de choisir l'un plutôt que l'autre, n'importe lequel fera l'affaire, et dans le cas qui nous intéresse tu as un critère supplémentaire : les autres faut y mettre du temps en plus pour les trouver, donc autant rester sur ce qu'on a sous la main.
Site perso
Recommandations pour débattre sainement
Références récurrentes :
The Cambridge Handbook of Expertise and Expert Performance
L’Art d’avoir toujours raison (ou ce qu'il faut éviter pour pas que je vous saute à la gorge {^_^})
Presque sauf que: le remplacement d'un fichier, le clear() global, et toute autre fonction de retrait provoque le close(). Mais ça, tu peux faire aussi. Le plus important est que l'on a un nouveau type, et quand j'écris Array<File>, j'ai des fichiers automatiquement nettoyés, et je te laisse imaginer la suite pour Array<Socket>, Array<T*>, ... Si je ne m'abuse, ce que tu fais, c'est au moment de la création de ton objet. Nous c'est au niveau du type. Et on peut aller bien plus loin.
Non absolument pas. (Un if ne compilerait pas; et ce n'est pas le point). Avec les templates on continue de respecter l'OCP : on définit des points de variations et pour ces points de variations, on adapte. Tout comme avec le polymorphisme d'inclusion dynamique, sauf qu'il est paramétrique et statique.
NB: on évite de faire un remplacement sur toute la classe, on va plutôt prévoir un Nettoyeur, et la classe tableau ira chercher le bon nettoyeur en fonction du type qui lui est passé. Soit:
Bref, lecture ici: http://alp.developpez.com/tutoriels/traitspolicies/
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 template <typename T> struct Cleaner { static void clean(T & v) {} } template <> struct Cleaner<FILE> { static void clean(FILE * f) { if (f) { fclose(f);} } } template <typename T> struct Cleaner<T*> { static void clean(T * p) { delete p; } } template <typename T> struct OwningArray { typedef Cleaner<T> Cleaner_t; ... iterator erase(iterator it) { Cleaner_t::clean(*it); std::move_backward(it+1, end(), it); } iterator erase(iterator first, iterator last) { std::for_each(first, last, &Cleaner_t::clean); std::move_backward(last, end(), first); } ... // bon, c'est du vite pondu, il manque la gestion de l'assign de l'itérator qui doit nettoyer aussi };
Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...
@ DonQuiche : http://fr.wikipedia.org/wiki/Perfection
Il n'est pas question de faire un quelconque comparatif. La perfection ne dépend pas des autres, uniquement du contexte en place. Si tu parles de comparatif, ta notion de perfection est probablement en fait le concept de "meilleur".La perfection caractérise un être ou un objet idéal, c'est-à-dire qui réunit toutes les qualités et n'a pas de défaut.
Site perso
Recommandations pour débattre sainement
Références récurrentes :
The Cambridge Handbook of Expertise and Expert Performance
L’Art d’avoir toujours raison (ou ce qu'il faut éviter pour pas que je vous saute à la gorge {^_^})
C++ est portable et normalisé. C++ est haut niveau, c'est l'un des langages qui permet de plus d'abstraction et de réutilisation de code. Par sa compatibilité avec C, il augmente ses possibilités de réutilisation de code et est également bas niveau si besoin. C++ est rapide (développement et exécution).
Je connais un peu Python, il a certains aspects dynamiques sympathiques mais rien qui me manquent en C++ (même pas la possibilité de créer des variables dynamiquement et la facilité de création de plugins).
Très bien, si c'est que ça alors revient sur mon exemple avec les interfaces. Et plutôt que d'utiliser un LinkedList pour les deux en réécrivant uniquement celui des FileReader, tu auras une classe "FileList extends LinkedList<FileReader>" qui surcharge pareil mais que tu peux réutiliser où tu veux. Et au lieu de l'imposer à toute liste de fichier en l'imposant au template de base, tu te contentes de fournir une classe spécialisée que ceux qui en auront besoin pourront utiliser, et les autres n'auront pas ce soucis. Parce que tu m'excuseras, mais dans ta spécialisation, tu changes le nom et tu la fait étendre la classe de base et tu obtiens la fonctionnalité. La seule différence étant qu'avec la spécialisation tu imposes la modif, donc tu part du principe que c'est une hypothèse de même niveau que le niveau d'abstraction de la classe générale. Mais si c'est le cas, en Java tu fais un if(... instanceof ...)" et tu obtiens pareil, comme dans mon exemple.
Si, un if compile. C'est du code basique. Je sais pas si C++ en est capable, mais en Java ça compile et même mieux, ça tourne! J'ai écrit celui-là à la volée donc je sais pas si lui compile, mais si tu veux je t'en fais un sous eclipse. Ça sera pas la première fois.
Donc pareil que mon if, la seule différence étant qu'une instance aura soit le bout de code, soit ne l'aura pas, plutôt que d'avoir un if qui exécute ou non le bout de code. Donc question de perf uniquement.
Pareil en Java. Pas avec le même code, mais oui on peut définir des classes spécialisées à fournir pour customiser l'objet. Les patterns du style visitor and cie sont tout ausi applicables en Java qu'en C++.
En me basant sur les exemples donnés et ce tuto.
L'exemple principal est sur faire la différence entre pointeurs et non pointeurs... en Java c'est transparent, on ne gère aucun pointeur (ou on pourrait dire qu'on ne fait que ça), donc l'exemple n'a juste aucun sens pour une comparaison avec du Java et donc ne permet pas de justifier qu'une telle fonctionnalité manque à Java. Du reste, j'ai l'impression qu'on fait grosso modo pareil que de simples fonctions. En particulier, le coup de "je met la valeur à false, mais si c'est tel type alors je met true" me fait penser à définir directement la valeur avec un opérateur ternaire sur un instanceof ou équivalent (pour Java), tout en pouvant la faire constante aussi (car évaluée à l'instanciation). Le seul soucis en Java étant qu'on ne peut pas récupérer une Class<T> à partir d'un generics, mais on connait depuis longtemps une alternative pour ça : on fournit la class au constructeur à l'instanciation. C'est pas "propre", mais ça l'est pas moins que des définitions supplémentaires avec davantage de lignes de code et de surcroit de la redondance. Question de point de vue.
Donc j'en vois toujours pas l'utilité en dehors de la perf (et encore faut trouver les cas où la différence est significative). Ou dit autrement, j'ai l'impression que c'est là pour régler un problème qui n'existe pas en Java. Je serais curieux de voir un exemple où ça apporte un avantage net sans avoir quoi que ce soit à voir avec la différenciation pointeur/pas pointeur (ou référence/pas référence). Quand je vois les classes de traits ou de politiques, je me dis que c'est une autre façon de faire ce qu'on sait déjà faire avec des classes :
- la classe de trait -> instanceof
- la classe de politique -> je te file une instance à utiliser qui implémente les fonctions dont tu as besoin
Là où je peux voir un truc intéressant, mais à condition que j'ai bien compris, c'est quand je vois le cas où on utilise une méthode statique de politique. Pas parce qu'on peut utiliser une méthode statique, parce que toute méthode statique peut être rendue non statique dans un objet dédié sans perte significative (question d'organisation du code). Mais parce que, si j'ai bien compris, la ligne "Operation::Accumuler(Resultat, *Debut);" définit grosso modo une interface à la volée. Donc plutôt que de définir explicitement une interface qui doit être explicitement implémentée par les classes filles, je dis que "tout objet qui implémente une méthode ayant cette signature est une instance de cette interface". Si c'est bien comme ça que ça marche, alors ça oui c'est intéressant : avoir la possibilité de catégoriser une instance de la sorte sans qu'elle ait besoin d'étendre explicitement l'interface, ça c'est quelque chose que j'aimerai bien voir en Java (avec quelques réserves cela dit, parce qu'on mélange les concepts sans contrôle aucun, disons plutôt que j'aimerai pouvoir définir des équivalences). Maintenant je dis ça parce que je vois nulle part dans l'exemple la définition de Operation, donc je pars du principe que c'est quelque chose d'implicite dans le genre.
Si tu penses que quelque chose est parfait sans donner aucun contexte, tu devrais revoir ta conception du monde. Si la perfection se définit par avoir toutes les qualités et aucun défaut, il faut bien voir que les qualités et défauts n'existent pas en dehors d'un contexte : avoir les cheveux long est un défaut si tu travailles en cuisine où il faut éviter d'avoir ses cheveux dans la marmitte. C'est une qualité si tu participes à un concours de mannequin où les cheveux longs donnent un attrait supplémentaire. En dehors de tout contexte, ni qualité ni défaut, donc pas de perfection (ni son contraire) ou que ce soit. Si pour une personne, le C++ est parfait, c'est que dans son contexte ça l'est. Le contexte évoluant avec le vécu, le jour où le C++ ne sera plus parfait elle pourra toujours aller voir ailleurs.
Et pour ma part, non le C++ n'est pas parfait, sinon j'y serai resté plutôt que de switcher sur Java. Et bien que Java ne soit pas parfait non plus (pour moi, toujours), c'est toujours mieux que le C++ (encore une fois pour moi). Maintenant, si tu ne peux pas concevoir qu'une opinion n'est pas forcément vraie en dehors du contexte de celui qui la donne, et donc qu'il n'y a pas grand intérêt à la juger en dehors de ce contexte, alors je comprend que tu n'arrives pas à suivre.
Site perso
Recommandations pour débattre sainement
Références récurrentes :
The Cambridge Handbook of Expertise and Expert Performance
L’Art d’avoir toujours raison (ou ce qu'il faut éviter pour pas que je vous saute à la gorge {^_^})
Citation du tuto :
Donc on fait pareil autrement. L'implémentation est différente, mais l'utilité est la même.Les politiques vous rappellent peut-être le design pattern Strategy, car en réalité c'est ce à quoi elles correspondent. Cependant, elles exploitent les possibilités du C++ que n'offrent certains autres langages, ce qui les rend cependant différentes d'implémentations du design pattern Strategy que l'on pourrait trouver en Java, par exemple.
Site perso
Recommandations pour débattre sainement
Références récurrentes :
The Cambridge Handbook of Expertise and Expert Performance
L’Art d’avoir toujours raison (ou ce qu'il faut éviter pour pas que je vous saute à la gorge {^_^})
Question naïve : le instanceof de Java ne va pas empêcher l'utilisateur d'ajouter son type ? Ou alors son type va devoir implements le type que tu testes mais ça ne marche pas pour les classes qui existent déjà et qui sont hors du contrôle de l'utilisateur (dans ce cas l'utilisateur devra l'encapsuler, ce qui est assez verbeux).
Voici un exemple qui utilise un type trait pour savoir quel code lancer :
Note: dans la vrai vie on utilise std::pow qui fait la bonne chose avec les puissances inférieures à 1.
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 // Floating point template <class T> T nth_root(T const & a, std::size_t const n, is_integer<false>) { T r = 1; T delta = r; T previous_delta = 0; while (std::abs(delta + previous_delta) > std::numeric_limits<T>::epsilon()) { previous_delta = delta; delta = r; // Compute r = (T(1) / T(n)) * (T(n - 1) * r + (a / T(std::pow(r, n - 1)))); // Get delta delta -= r; } return r; } // Integer // Actual algorithm works only floating point, use long double for internal computation template <class T> long double nth_root(T const & a, std::size_t const n, is_integer<true>) { return nth_root(static_cast<long double>(a), n, is_integer<false>()); } template <class T> auto nth_root(T const & a, std::size_t const n) -> decltype(hnc::math::nth_root(a, n, is_integer<std::numeric_limits<T>::is_integer>())) { // ... return nth_root(a, n, is_integer<std::numeric_limits<T>::is_integer>()); }
L'utilisateur peut spécialiser std::numeric_limits<T>::is_integer> pour ses propres types et n'a pas besoin de modifié nth_root.
Tu as trois interfaces différentes. J'en ai qu'une au final, mais 3 spécialisations. Tu manipules un FileList, je manipules un Array<FILE>. Et même je peux avoir un Toto<T> qui contienne un Array<T>. Et donc automatiquement, un Toto<FILE> va contenir un Array<FILE> qui est une spécialisation. Et effectivement, je considère que tous mes Array<FILE> doivent se comporter de la même manière. C'est ce que je demande à mon système de type.
La spécialisation n'est pas forcément au niveau du tableau mais au niveau d'une politique du tableau, mais la spécialisation des sous-parties va impacter la comportement du truc de plus haut niveau, et c'est ce que je veux.
Comprends que l'exemple donné est un exemple simple. Et que les types reçus en paramètre template ne vont pas nécessairement avoir une interface commune. On peut recevoir des fichiers, des pointeurs, des pots de peintures, et ils se feront fermer(), detruire(), ou remettre_sur_l_etagere(), ou même .... des entiers. En C++, il n'y a pas de god object qui permette de downcaster. Et donc, on ne peut pas faire un "if instanceof, downcaste et applique une fonction qui n'existe pas chez les autres". Et "if" n'est pas extensible. C'est en violation avec l'OCP.
Non. Question d'OCP. Un if n'est pas extensible sans modification du code.
Non, c'est dynamique et tu es prisonnier du paradigme objet. Avec les templates, on lorgne du côté du Duck Typing.
a- c'est pour ça que j'ai présenté des ressources que même vous en Java devez manipuler différemment: soit des entiers (natifs), soit des fichiers, soit des pots de peintures, ... Et pas des pointeurs car la pertinence de distinguer ce que l'on fait peut vite vous échapper. Les pointeurs sont un cas particulier fréquent chez nous. Mais ils ne sont qu'un cas particulier.
b- La vrai généricité:
- un tableau qui sait finaliser ce qu'on lui enlève
- un algorithme de recherche dichotomique générique (oui, je sais les œillères OO du Java vous empêchent d'avoir cela en interdisant les fonctions libres ; et donc tous les algos sont dupliqués dans tous les conteneurs, même lorsqu'une factorisation serait possible entre conteneurs qui ne font que se ressembler parfois sur une propriété, parfois sur une autre) qui sache travailler sur des itérateurs à accès direct (tableaux) comme sur des itérateurs séquentiels (liste chainée)
- un algorithme générique de décodage de données binaires qui sache s'adapter à des nombres, des chaines, ou autre, et sans un seul if pour extraire ce que l'on attend à l'endroit courant. A la place: j'ai une liste de types et le code associé à chaque type de la liste s'exécute tout seul sans un seul if pour déclencher l'adaptation sur le point de variation.
- une comparaison d'égalité entre nombres qui sache distinguer entiers et doubles pour les comparer à un epsilon près ; comparaison qui va servir à implémenter un ensemble sans redondances, ou l'algorithme générique qui correspond à uniq, ou encore une interface simplifiée pour des tests unitaires.
Parce que oui, on peut tout adapter avec des if, mais quand le compilo peut savoir exactement ce qui est manipulé par construction du programme, il n'y a pas besoin d'exécuter le moindre if. Quand on dit de faire l'addition de 2 nombres, encore heureux que le compilateur n'ait pas généré du code qui commence par vérifier le type des nombres impliqués avant d'appeler la fonction d'addition idoine. Et on peut aller plus loin en refusant (à la compilation !!) l'addition d'une masse avec une longueur, tout en autorisant d'ajouter un m^3/g/s^2 + m^3/g/s^2 (chose qui nous est offerte par boost::unit grâce aux templates).
C'est important ça : le refus à la compilation de choses qui ne sont pas valides selon l'algo générique. Si l'algo générique stipule que le tri n'est possible que sur des conteneurs supportant l'accès direct (ou au pire bidirectionnel), alors il refusera tout tentative de tri sur des conteneurs dont les itérateurs n'offrent qu'une passe en avant (p.ex. un curseur dans un fichier, ou un itérateur qui simule des données générées à la volée).
c- Oui, c'est le duck typing. On le supporte grâce aux templates, et éventuellement de la spécialisation quand il y a besoin de supporter des canards unijambistes.
Blog|FAQ C++|FAQ fclc++|FAQ Comeau|FAQ C++lite|FAQ BS|Bons livres sur le C++
Les MP ne sont pas une hotline. Je ne réponds à aucune question technique par le biais de ce média. Et de toutes façons, ma BAL sur dvpz est pleine...
Vous avez un bloqueur de publicités installé.
Le Club Developpez.com n'affiche que des publicités IT, discrètes et non intrusives.
Afin que nous puissions continuer à vous fournir gratuitement du contenu de qualité, merci de nous soutenir en désactivant votre bloqueur de publicités sur Developpez.com.
Partager