This shows you the differences between two versions of the page.
Next revision | Previous revision | ||
in204:tds:sujets:td6:part2 [2019/10/15 07:25] bmonsuez created |
in204:tds:sujets:td6:part2 [2022/11/18 10:49] (current) |
||
---|---|---|---|
Line 1: | Line 1: | ||
+ | ====== Partie II – Spécialisation par contraintes ====== | ||
[[in204:tds:sujets:td6|TD6]] | [[in204:tds:sujets:td6|TD6]] | ||
+ | |||
+ | ===== Question n°1 ===== | ||
+ | |||
+ | Nous considérons toujours notre code affichant le contenu d'un conteneur tel que défini dans la [[in204:tds:sujets:td6:part1|première partie]]. | ||
+ | |||
+ | <code cpp> | ||
+ | template<typename containerT, typename charT, typename traits = std::char_traits<charT>> | ||
+ | std::basic_ostream<charT, traits>& operator << (std::basic_ostream<charT, traits>& aStream, const containerT& aContainer) | ||
+ | { | ||
+ | aStream << "{"; | ||
+ | auto end = aContainer.end(); | ||
+ | for(auto it = aContainer.begin(); it != end;) | ||
+ | { | ||
+ | aStream << *it ++; | ||
+ | if(it != end) | ||
+ | aStream << ", "; | ||
+ | } | ||
+ | aStream << "}"; | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | ==== Question n°1.1 ===== | ||
+ | |||
+ | Déterminer ce qui va caractériser un conteneur pouvant être pris comme argument par la fonction précédente ? | ||
+ | |||
+ | <hidden Correction> | ||
+ | |||
+ | En fait, il est attendu que le conteneur fournisse deux méthodes : | ||
+ | |||
+ | * `begin()`: retournant un itérateur marquant le début de la séquence, | ||
+ | * `end()`: retournant un itérateur marquant le fin de la séquence. | ||
+ | |||
+ | L'itérateur qui est retourné doit supporter : | ||
+ | * l'accès en lecture des données stockées dans le conteneur, | ||
+ | * le parcours en avant. | ||
+ | |||
+ | Il n'a pas besoin de supporter le multipasse. | ||
+ | |||
+ | </hidden> | ||
+ | |||
+ | |||
+ | ==== Question n°1.2 ===== | ||
+ | |||
+ | Implanter un concept qui permet de formaliser les exigences que vous venez de formuler ? | ||
+ | |||
+ | <hidden Correction> | ||
+ | <code cpp> | ||
+ | template<typename T> | ||
+ | concept Browsable = requires (const T& a) | ||
+ | { | ||
+ | { a.begin() } -> std::forward_iterator; | ||
+ | { a.end() } -> std::forward_iterator; | ||
+ | }; | ||
+ | </code> | ||
+ | |||
+ | </hidden> | ||
+ | |||
+ | ==== Question n°1.3 ===== | ||
+ | |||
+ | Ajouter les contraintes à l'opérateur %%<<%% qui a été défini pour un conteineur. | ||
+ | |||
+ | <hidden Correction> | ||
+ | <code cpp> | ||
+ | template<typename containerT, typename charT, typename traits = std::char_traits<charT>> | ||
+ | requires Browsable<containerT> | ||
+ | std::basic_ostream<charT, traits>& operator << (std::basic_ostream<charT, traits>& aStream, const containerT& aContainer) | ||
+ | { | ||
+ | aStream << "{"; | ||
+ | auto end = aContainer.end(); | ||
+ | for(auto it = aContainer.begin(); it != end;) | ||
+ | { | ||
+ | aStream << *it ++; | ||
+ | if(it != end) | ||
+ | aStream << ", "; | ||
+ | } | ||
+ | aStream << "}"; | ||
+ | return aStream; | ||
+ | } | ||
+ | </code> | ||
+ | </hidden> | ||
+ | |||
+ | |||
+ | ==== Question n°1.4 ===== | ||
+ | |||
+ | Introduisez une implantation de l'opérateur `<<` qui pour tout type affiche 'NONE'. | ||
+ | |||
+ | <hidden Correction> | ||
+ | <code cpp> | ||
+ | template<typename T, typename charT, typename traits = std::char_traits<charT>> | ||
+ | std::basic_ostream<charT, traits>& operator << (std::basic_ostream<charT, traits>& aStream, const T& aContainer) | ||
+ | { | ||
+ | std::cout << "NONE"; | ||
+ | return aStream; | ||
+ | } | ||
+ | </code> | ||
+ | </hidden> | ||
+ | |||
+ | |||
+ | ===== Question n°2 ==== | ||
+ | |||
+ | Nous avons deux fonctions pour effectuer un tri, l'algorithme simple de tri que nous avons implanté et l'algorithme ``std::sort`` fourni par la STL. | ||
+ | |||
+ | Cependant notre algorithme fonctionne pour des itérateurs qui ne supportent pas l'accès indexé tandis que l'algorithme ``std::sort`` est un quicksort et nécessite un accès indexé mais qui est plus rapide. | ||
+ | |||
+ | Modifier le code de ``std::simple_sort`` pour appeller ``std::sort`` si jamais les itérateurs sont des itérateurs supportant l'accès direct (``std::random_access_iterator``). | ||
+ | |||
+ | <hidden Correction> | ||
+ | <code cpp> | ||
+ | template<typename iterator> | ||
+ | void simple_sort(iterator start, iterator end) | ||
+ | requires(std::forward_iterator<iterator> && ! std::random_access_iterator<iterator> && std::input_or_output_iterator<iterator>) | ||
+ | { | ||
+ | std::cout << "Insertion Sort\n"; | ||
+ | for(;start != end; start ++) | ||
+ | { | ||
+ | auto it = start; it++; | ||
+ | for(;it != end; it ++) | ||
+ | { | ||
+ | // Compare si les deux elements sont dans le bon ordre. | ||
+ | if (*start > *it) | ||
+ | std::swap(*start, *it); | ||
+ | } | ||
+ | } | ||
+ | } | ||
+ | template<typename iterator> | ||
+ | void simple_sort(iterator start, iterator end) | ||
+ | requires(std::random_access_iterator<iterator> && std::input_or_output_iterator<iterator>) | ||
+ | { | ||
+ | std::cout << "Quicksort\n"; | ||
+ | return std::sort(start, end); | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | Et nous exécutons le code suivant : | ||
+ | |||
+ | <code cpp> | ||
+ | { | ||
+ | std::list<int> l = {1, 7, 3, 4, 9, 2, 5}; | ||
+ | std::vector<int> v(l.begin(), l.end()); | ||
+ | simple_sort(l.begin(), l.end()); | ||
+ | std::cout << l << std::endl; | ||
+ | simple_sort(v.begin(), v.end()); | ||
+ | std::cout << v << std::endl; | ||
+ | </code> | ||
+ | |||
+ | qui génèrera la sortie suivante: | ||
+ | <code> | ||
+ | Insertion Sort | ||
+ | {1, 2, 3, 4, 5, 7, 9} | ||
+ | Quicksort | ||
+ | {1, 2, 3, 4, 5, 7, 9} | ||
+ | </code> | ||
+ | |||
+ | </hidden> | ||
+ | |||
+ |