This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision | ||
in204:tds:sujets:td3:part2 [2019/10/05 13:24] bmonsuez [Question n°2.3] |
in204:tds:sujets:td3:part2 [2022/11/18 10:49] (current) |
||
---|---|---|---|
Line 44: | Line 44: | ||
<hidden Correction> | <hidden Correction> | ||
+ | |||
Supposons d'abord que nous souhaitons appelé la fonction ''generic::populate_with_randoms'' en lui passant un tableau de type ''std::string'' avec le code suivant : | Supposons d'abord que nous souhaitons appelé la fonction ''generic::populate_with_randoms'' en lui passant un tableau de type ''std::string'' avec le code suivant : | ||
Line 84: | Line 85: | ||
} | } | ||
</code> | </code> | ||
- | |||
</hidden> | </hidden> | ||
===== Question n°2 ===== | ===== Question n°2 ===== | ||
Line 93: | Line 93: | ||
==== Question n°2.1 ==== | ==== Question n°2.1 ==== | ||
- | Ecrire la classe ''greater_traits'' en complétant ce qui manque dans la classe suivante: | + | Ecrire la classe ''greater_traits'' en complétant ce qui manque dans la classe suivante: |
<code cpp> | <code cpp> | ||
Line 100: | Line 100: | ||
{ | { | ||
public: | public: | ||
- | static bool is_greater(T aValue, T anotherValue) {...} | + | static bool is_greater(T aValue, T anotherValue) |
+ | { | ||
+ | ... | ||
+ | } | ||
}; | }; | ||
</code> | </code> | ||
- | ==== Question n°2.2 ==== | + | <hidden Correction> |
- | Modifier la fonction ''simple_sort'' pour qu’elle prenne un argument supplémentaire | + | La classe ''greater_test'' teste si ''aValue > anotherValue'' et retourne ''true'' si la condition est vérifiée et ''false'' si cette condition n'est pas vérifiée. |
- | qui est la classe fournissant l’opération de comparaison. | + | |
- | ==== Question n°2.3 ==== | + | <code cpp> |
+ | template<typename T> | ||
+ | struct greater_traits | ||
+ | { | ||
+ | public: | ||
+ | static bool is_greater(T aValue, T anotherValue) | ||
+ | { | ||
+ | return aValue > anotherValue; | ||
+ | } | ||
+ | }; | ||
+ | </code> | ||
- | Proposer une classe ''lower_traits'' qui inverse l’ordre de tri. | + | La méthode ''is_greater'' est précédée du mot clé ''static'', ce qui signifie que cette méthode est une méthode statique aussi appellée méthode de classe. A la différence des méthodes habituelles de l'objet, cette méthode ne peut accéder qu'aux champs statique statiques de la classe. En conséquence, cette méthode n'a pas besoin qu'un objet soit créé pour pouvoir être appelé. Elle peut simplement être appellée selon la syntaxe suivante : |
+ | |||
+ | <code cpp> | ||
+ | |||
+ | int main() | ||
+ | { | ||
+ | greater_traits<int>::is_greater(3, 2); | ||
+ | } | ||
+ | </code> | ||
- | <hidden Correction> | ||
</hidden> | </hidden> | ||
+ | |||
+ | |||
+ | |||
+ | ==== Question n°2.2 ==== | ||
+ | |||
+ | Modifier la fonction ''simple_sort'' pour qu’elle prenne un paramètre de type supplémentaire | ||
+ | qui est la classe fournissant l’opération de comparaison. | ||
<hidden Correction> | <hidden Correction> | ||
+ | |||
+ | Nous ajoutons à la liste des paramètres de types de la fonction, un paramètre supplémentaire ''sortTraits'' qui correspond au type d'une classe qui doit exposer une méthode statique permettant de tester si une valeur est plus grande qu'une autre valeur pour des valeurs de types ''T'' et ayant pour nom ''is_greater''. | ||
+ | |||
+ | Cependant, ce paramètre de type est facultatif, par défaut, nous considérons que ce paramètre est initialisé à ''greater_traits<T>''. | ||
+ | |||
+ | Maintenant, nous avons un premier souci. Au moins un paramètre de la fonction doit faire référence à un paramètre de types. Cependant, nous n'avons aucun paramètre qui n'a le type ''sortTraits''. Enfin, nous ne créons aucun objet de type ''sortTraits'' que nous allons passer en paramètre. Pour contourner ce problème, nous allons ajouter un paramètre supplémentaire à la fonction qui sera un pointeur sur un objet de type ''sortTraits'' et qui sera initialisé à ''NULL''. | ||
+ | <code cpp> | ||
+ | void simple_sort(std::vector<T>& theValues, sortTraits* = NULL) | ||
+ | </code> | ||
+ | |||
+ | Comme nous n'utilisons jamais ce paramètre, nous ne donnons même pas de nom à ce paramètre ! Il n'est là que pour respecter la règle qui impose que tout paramètre de type doit être référencé directement ou undirectement par au moins un paramètre de la fonction. | ||
+ | |||
+ | Nous modifions ensuite l'opération de comparaison pour désormais faire appel à la méthode statique ''is_greater'' de la classe de type ''sortTraits''. | ||
+ | |||
+ | <code cpp> | ||
namespace generic | namespace generic | ||
{ | { | ||
... | ... | ||
- | template<typename T, typename sortTraits = lower_traits<T>> | + | template<typename T, typename sortTraits = greater_traits<T>> |
- | | + | void simple_sort(std::vector<T>& theValues, sortTraits* = NULL) |
- | void simple_sort(std::vector<T>& theValues) | + | |
{ | { | ||
- | sortTraits sort_traits; | ||
- | // Initialise la classe comparateur. | ||
int length = theValues.size(); | int length = theValues.size(); | ||
for(int i = 0; i < length-1; i ++) | for(int i = 0; i < length-1; i ++) | ||
Line 132: | Line 170: | ||
for(int j= i+1; j < length; j++) | for(int j= i+1; j < length; j++) | ||
{ | { | ||
- | if(sort_traits.compare(theValues[i], theValues[j]) > 0) | + | if(sortTraits::is_greater(theValues[i], theValues[j])) |
std::swap(theValues[i], theValues[j]); | std::swap(theValues[i], theValues[j]); | ||
} | } | ||
Line 139: | Line 177: | ||
... | ... | ||
} | } | ||
+ | </code> | ||
</hidden> | </hidden> | ||
+ | |||
+ | ==== Question n°2.3 ==== | ||
+ | |||
+ | Proposer une classe ''lower_traits'' qui inverse l’ordre de tri. | ||
+ | |||
+ | <hidden Correction> | ||
+ | |||
+ | Il suffit de dupliquer la classe ''greater_traits'' et de remplacer l'opérateur ''>'' par l'opérateur ''<''. | ||
+ | |||
+ | <code cpp> | ||
+ | template<typename T> | ||
+ | struct lower_traits | ||
+ | { | ||
+ | public: | ||
+ | static bool is_greater(T aValue, T anotherValue) | ||
+ | { | ||
+ | return aValue > anotherValue; | ||
+ | } | ||
+ | }; | ||
+ | </code> | ||
+ | </hidden> | ||
+ | |||
==== Question n°2.4 ==== | ==== Question n°2.4 ==== | ||
Line 163: | Line 224: | ||
} | } | ||
</code> | </code> | ||
+ | |||
+ | <hidden Compléments> | ||
+ | |||
+ | Nous avons vu comment paramètrer la fonction ''simple_sort'' par un opérateur de comparaison. Cependant, l'opérateur de comparaison est un opérateur qui expose une méthode ''statique''. Nous ne pouvons pas choisir un opérateur de conversion dynamique, ie. dont nous pouvons au moment de l'exécution décider de l'ordre, si c'est un tri ascendant ou descendant. Nous pourrions imagine une telle classe. | ||
+ | |||
+ | <code cpp> | ||
+ | template<typename T> | ||
+ | struct greater_or_lower_traits | ||
+ | { | ||
+ | private: | ||
+ | bool m_isAscending; | ||
+ | | ||
+ | public: | ||
+ | greater_or_lower_traits(): | ||
+ | m_isAscending(false) {} | ||
+ | explicit greater_or_lower_traits(bool isAscending): | ||
+ | m_isAscending(isAscending) {} | ||
+ | | ||
+ | set(bool isAscending) { m_isAscending = isAscending; } | ||
+ | get() const { return m_isAscending ; } | ||
+ | | ||
+ | bool is_greater(T aValue, T anotherValue) | ||
+ | { | ||
+ | if(!m_isAscending) | ||
+ | return aValue < anotherValue; | ||
+ | return aValue > anotherValue; | ||
+ | } | ||
+ | }; | ||
+ | </code> | ||
+ | |||
+ | Sauf que dans ce cas, il est nécessaire d'avoir un champ. Donc la méthode ''is_greater'' ne peut pas être une méthode de classe. | ||
+ | |||
+ | Pour ce faire, nous modifions le contrat relatif à la classe ''traits'' définissant l'ordre sur les valeurs de type ''traits'' comme suit : | ||
+ | * la méthode ''is_greater'' n'est plus une méthode de classe. | ||
+ | * l'objet expose __**un constructeur par défaut**__. | ||
+ | * l'objet supporte la recopie. | ||
+ | |||
+ | Nous devons modifier en conséquence les classes ''greater_traits'' et ''lower_traits'' pour transformer la méthode ''statique'' en méthode standard : | ||
+ | |||
+ | <code cpp> | ||
+ | template<T> | ||
+ | struct greater_traits | ||
+ | { | ||
+ | public bool is_greater(T aValue, T anotherValue) const | ||
+ | { | ||
+ | return aValue > anotherValue; | ||
+ | } | ||
+ | }; | ||
+ | template<T> | ||
+ | struct lower_traits | ||
+ | { | ||
+ | public bool is_lower(T aValue, T anotherValue) const | ||
+ | { | ||
+ | return aValue < anotherValue; | ||
+ | } | ||
+ | }; | ||
+ | </code> | ||
+ | |||
+ | Avec ces deux conditions, il est possibles de redéfinir la définition de la fonction ''simple_sort'' comme suit : | ||
+ | |||
+ | <code cpp> | ||
+ | namespace generic | ||
+ | { | ||
+ | ... | ||
+ | |||
+ | template<typename T, typename sortTraits = greater_traits<T>> | ||
+ | void simple_sort(std::vector<T>& theValues, | ||
+ | sortTraits traits = sortTraits()) | ||
+ | { | ||
+ | int length = theValues.size(); | ||
+ | for(int i = 0; i < length-1; i ++) | ||
+ | { | ||
+ | for(int j= i+1; j < length; j++) | ||
+ | { | ||
+ | if(traits.is_greater(theValues[i], theValues[j])) | ||
+ | std::swap(theValues[i], theValues[j]); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | ... | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | Nous ajoutons un paramètre supplémentaire à la fonction ''simple_sort'' qui est le paramètre ''traits''. Ce paramètre a pour type ''sortTraits'' et correspond normalement à une instance d'une classe traits qui fournit la méthode ''is_greater'' tel que définit précédemment. Cependant, il n'est pas nécessaire de passer ce paramètre quand nous utilisons les classes traits ''greater_traits'' ou ''lower_traits''. Dans ce cas, nous affectons à ce paramètre l'objet de type ''sortTraits'' qui est initialisé par le constructeur par défaut. | ||
+ | |||
+ | <code cpp> | ||
+ | std::vector<int> vector; | ||
+ | ... | ||
+ | simple_sort(vector); | ||
+ | </code> | ||
+ | |||
+ | Dans ce cas, quand le compilateur compile le code ''simple_sort'', il peut inférer que : | ||
+ | * T dénote le type entier ''int'', | ||
+ | * aucun argument n'est passé pour le paramètre ''traits'', le type n'est pas résolu. | ||
+ | Il reste donc à résoudre le type ''sortTraits''. | ||
+ | * aucun type n'est passé en paramètre à la fonction ''simple_sort'' | ||
+ | * un type par défaut est spécifié pour le type ''sortTraits''. Dans ce cas, le compilateur affecte à ''sortTraits'' le type par défaut, c'est-à-dire ''greater_traits<T>'' avec ''T==int'' qui se transforme en ''greater_traits<int>''. | ||
+ | Une fois le type ''sortTraits'' inféré, il ne reste plus qu'à affecter à ''traits'' la valeur par défaut. Cette valeur par défaut est défini comme étant égal une instance d'objet de type ''sortTraits'' initialisé par son constructeur par défaut : ''sortTraits()''. Ce qui dans le cas correspond à ''greater_traits<int>()''. Le compilateur génère donc l'appel suivant : | ||
+ | |||
+ | <code cpp> | ||
+ | std::vector<int> vector; | ||
+ | ... | ||
+ | simple_sort<int, greater_traits<int>)(vector, greater_traits<int>()); | ||
+ | </code> | ||
+ | |||
+ | Considérons désormais l'appel suivant: | ||
+ | |||
+ | <code cpp> | ||
+ | std::vector<int> vector; | ||
+ | ... | ||
+ | simple_sort<int, lower_traits<int>>(vector); | ||
+ | </code> | ||
+ | |||
+ | Dans ce cas, quand le compilateur compile le code ''simple_sort'', il peut inférer que : | ||
+ | * T dénote le type entier ''int'', | ||
+ | * aucun argument n'est passé pour le paramètre ''traits'', le type n'est pas résolu. | ||
+ | Il reste donc à résoudre le type ''sortTraits''. | ||
+ | * le type ''lower_traits<int>'' est passé en paramètre à la fonction ''simple_sort''. Dans ce cas, le compilateur affecte à ''sortTraits'' le type par défaut, c'est-à-dire ''lower_traits<int>''. | ||
+ | |||
+ | Une fois le type ''sortTraits'' inféré, il ne reste plus qu'à affecter à ''traits'' la valeur par défaut. Cette valeur par défaut est défini comme étant égal une instance d'objet de type ''sortTraits'' initialisé par son constructeur par défaut : ''sortTraits()''. Ce qui dans le cas correspond à ''lower_traits<int>()''. Le compilateur génère donc l'appel suivant : | ||
+ | |||
+ | |||
+ | <code cpp> | ||
+ | std::vector<int> vector; | ||
+ | ... | ||
+ | simple_sort<int, lower_traits<int>>(vector, lower_traits<int>()); | ||
+ | </code> | ||
+ | |||
+ | |||
+ | Enfin, si je souhaite effectuer un appel en passant une instance de l'objet ''greater_or_lower_traits'', il suffit d'écrire le code suivant : | ||
+ | |||
+ | <code cpp> | ||
+ | std::vector<int> vector; | ||
+ | |||
+ | greater_or_lower_traits traits(true); | ||
+ | simple_sort(vector, traits); | ||
+ | traits.set(false); | ||
+ | simple_sort(vector, traits); | ||
+ | </code> | ||
+ | |||
+ | Mais il est tout à fait possible d'écrire aussi : | ||
+ | |||
+ | <code cpp> | ||
+ | std::vector<int> vector; | ||
+ | |||
+ | simple_sort<int, greater_or_lower_traits <int>>(vector); | ||
+ | </code> | ||
+ | |||
+ | qui deviendra au moment de la compilation : | ||
+ | |||
+ | <code cpp> | ||
+ | std::vector<int> vector; | ||
+ | |||
+ | simple_sort<int, greater_or_lower_traits <int>>(vector, | ||
+ | greater_or_lower_traits<int>()); | ||
+ | </code> | ||
+ | |||
+ | |||
+ | </hidden> | ||
+ | |||
+ | |||