Vote des utilisateurs
0
0
Détails
Licence : Non renseignée
Mise en ligne le 6 novembre 2010
Langue : Français
Référencé dans
Navigation
[Programmation générique] Classe trait pour le type de passage d'argument
[Programmation générique] Classe trait pour le type de passage d'argument
Cette fonction permet de choisir automatiquement le meilleur moyen pour le passage d'argument. Elle est tirée de l'article d'Alp Mestan sur les classes de traits et de politiques.
Bonjour, Je vous propore un nouvel élément à utiliser : [Programmation générique] Classe trait pour le type de passage d'argument
Cette fonction permet de choisir automatiquement le meilleur moyen pour le passage d'argument. Elle est tirée de l'article d'Alp Mestan sur les classes de traits et de politiques.
Qu'en pensez-vous ?
Cette fonction permet de choisir automatiquement le meilleur moyen pour le passage d'argument. Elle est tirée de l'article d'Alp Mestan sur les classes de traits et de politiques.
Qu'en pensez-vous ?
Juste une question, y a t-il un avantage (d'un point de vue performance) lorsqu'il s'agit d'un int de le passer par copie plutôt que par référence constante ? N'est-ce pas la même taille ?
Les références et les pointeurs ont une taille hardware égale à la taille du mot natif. La taille d'un int est décorélée de la taille de mot native. Donc non, ce n'est pas toujours la même taille (en particulier en 64 bits, ou la taille d'une référence est 64 bits tandis que l'int est resté à 32 bits sur la plupart des plateformes).
Ceci dit, passer un int en référence constante (ou toute autre donnée dont la taille est égale ou inférieure à celle du mot natif) n'a pas vraiment de sens. En termes de performance, le gain (s'il y a, et j'en doute) est négligeable.
Ceci dit, passer un int en référence constante (ou toute autre donnée dont la taille est égale ou inférieure à celle du mot natif) n'a pas vraiment de sens. En termes de performance, le gain (s'il y a, et j'en doute) est négligeable.
Si je comprend bien, tu dis que cette classe de politique est uniquement didactique et n'est pas utilisée dans un cas réel ?
Non, il a juste dit que la taille d'un int n'est pas lié à la taille "natif" du système contrairement à celle d'un pointeur (d'un référence). Et que, pour les int mais aussi pour d'autre type, si la taille est inférieure à celle d'un pointeur le passage par référence ne serait pas nécessairement un gain. Et c'est exactement ce choix que permet de faire cet outil.
Ne serait-il pas mieux de comparer sizeof(T) avec sizeof(void*) plutôt que 8 ?
Ne serait-il pas mieux de comparer sizeof(T) avec sizeof(void*) plutôt que 8 ?
Non, il a juste dit que la taille d'un int n'est pas lié à la taille "natif" du système contrairement à celle d'un pointeur (d'un référence).
Et que, pour les int mais aussi pour d'autre type, si la taille est inférieure à celle d'un pointeur le passage par référence ne serait pas nécessairement un gain.
En termes de performance, le gain (s'il y a, et j'en doute) est négligeable.
Ne serait-il pas mieux de comparer sizeof(T) avec sizeof(void*) plutôt que 8 ?
Donc, je redemande, dans le cas de la copie, on peut gagner jusqu'à 7 octets sur les processeurs habituels. Est-ce que cela change quelque chose au performances ?
Le compilateur n'est-il pas déjà capable d'effectuer ce genre d'optimisation ?
N'est-ce pas alourdir le temps de compilation pour pas grand chose ?
Est-ce que la copie permet au compilateur de mieux optimiser (ce qui me semble être la raison qui justifie le plus ce type de politique) ?
Bien sur, je ne dis pas que cette classe désavantage un programme, je dis juste qu'il me semble qu'elle n'apporte pas de gain majeur. Ais-je tord ?
Bien sur que si, regarde boost::call_traits
Concernant, toutes ces histoires de copies et de passage par référence, peut-il y avoir un gain en déclarant la variable globale (pas de copie je crois) ? Bien sur, ce serait une optimisation du compilateur pas de moi (et je n'ai aucune idée de comment marche les variables globales).
ne faudrait-il pas en plus vérifier que le type est copie-constructible et sinon le passer par référence constante?
Non, elle a une réelle utilité.
Par exemple, on aurait pu réécrire std::vector<X>::push_back() ainsi :
(pour info, je rappelle que le prototype donnée par C++98 est push_back(T); même pas push_back(T& const)).
T n'est pas toujours un type de base ; ça peut être absolument n'importe quoi.
Plus exactement, ils font ce qu'ils veulent. Mais il y a tellement de code écrit qui définissent "typedef unsigned int uint32" que les vendeurs de compilateurs ont bien souvent décidé de laisser les int sur 32 bits. les long font généralement 64 bits (sur une architecture 64 bits, s'entend).
Il peut y avoir un gain infinitésimal, parce que le microprocesseur est optimisé pour lire des mots de 64 bits (en 64 bits, toujours). Du coup, lire un entier de 32 bits lui demande plus d'opérations microcodées. Ceci-dit, on parle de ce qui se passe dans le microcode, donc d'une fraction de l'horloge externe du microprocesseur. Il y a peu de chance pour que ça ait une répercussion à l'extérieur de celui-ci.
C'est ce que j'ai dit
Même réponse.
Non, hélas. Le compilateur a l'obligation de faire ce qu'on lui dit de faire. Il a peu de latitude pour modifier le code (il ne peut le faire que si le code modifié se comporte exactement comme l'original).
C'est un débat intéressant : le compilateur peut faire des élisions de code en cas de copie d'objets lourds, notamment s'ils ne sont pas modifiés. Sur le code suivant, un compilateur intelligent n'appellera le constructeur de la classe que s'il lui fait initialiser le paramètre à partir d'une valeur qui n'est pas du type du paramètre :
Le problème est que ce comportement n'est pas défini par le standard (ne pas comprendre que le standard le rend indéfini ; juste qu'il n'en parle pas, et du coup, bien que n'étant pas contraire au standard, il n'est pas obligatoire pour un vendeur de compilateur de l'implémenter).
Oui, parce qu'on parle de tous les types imaginables, pas seulement les types de base.
Ne t'inquiète pas, de telles raisons existent
Si tu souhaites passer par des variables globales, tu te trouves dans un autre problème : n'importe qui peut les modifier et tu sacrifie la réentrance (pas possible d'écrire une fonction récursive, pas possible d'écrire une fonction appelable depuis plusieurs threads,...).
Par exemple, on aurait pu réécrire std::vector<X>::push_back() ainsi :
Code : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 | template <class T> class vector { public: // il faudrait ajouter ConstParamType sur le modèle de ParamType void push_back(typename Calltraits<T>::ConstParamType value) { ... } }; |
T n'est pas toujours un type de base ; ça peut être absolument n'importe quoi.
Plus exactement, ils font ce qu'ils veulent. Mais il y a tellement de code écrit qui définissent "typedef unsigned int uint32" que les vendeurs de compilateurs ont bien souvent décidé de laisser les int sur 32 bits. les long font généralement 64 bits (sur une architecture 64 bits, s'entend).
Il peut y avoir un gain infinitésimal, parce que le microprocesseur est optimisé pour lire des mots de 64 bits (en 64 bits, toujours). Du coup, lire un entier de 32 bits lui demande plus d'opérations microcodées. Ceci-dit, on parle de ce qui se passe dans le microcode, donc d'une fraction de l'horloge externe du microprocesseur. Il y a peu de chance pour que ça ait une répercussion à l'extérieur de celui-ci.
C'est ce que j'ai dit
Même réponse.
Non, hélas. Le compilateur a l'obligation de faire ce qu'on lui dit de faire. Il a peu de latitude pour modifier le code (il ne peut le faire que si le code modifié se comporte exactement comme l'original).
C'est un débat intéressant : le compilateur peut faire des élisions de code en cas de copie d'objets lourds, notamment s'ils ne sont pas modifiés. Sur le code suivant, un compilateur intelligent n'appellera le constructeur de la classe que s'il lui fait initialiser le paramètre à partir d'une valeur qui n'est pas du type du paramètre :
Code : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | template <class T> T identify(T t) { return t; } struct X { ... (beaucoup de données) void do() { ... } }; int main() { X x; identity(x).do(); // aucune copie, grâce aux // différentes optimisations du compilateur. } |
Oui, parce qu'on parle de tous les types imaginables, pas seulement les types de base.
Ne t'inquiète pas, de telles raisons existent
Si tu souhaites passer par des variables globales, tu te trouves dans un autre problème : n'importe qui peut les modifier et tu sacrifie la réentrance (pas possible d'écrire une fonction récursive, pas possible d'écrire une fonction appelable depuis plusieurs threads,...).
Developpez.com décline toute responsabilité quant à l'utilisation des différents éléments téléchargés.