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> | ||
| + | |||
| + | |||