Loading lang_cpp_03_generic...
<algorithm>, afin de nous familiariser avec des fonctionnalités génériques simples.
template<typename T> inline const T & min(const T &a, const T &b) { return b<a ? b : a; }
template<typename T> inline const T & max(const T &a, const T &b) { return a<b ? b : a; }
<qui est propre aux chaînes et renvoie la référence constante désignant la plus petite ou la plus grande des deux.
<est applicable. Dans ces deux fonctions simplistes, les paramètres sont simplement consultés.
<du type concerné.
<.
[&](const auto &lhs, const auto &rhs) // left-hand-side, right-hand-side { return size(lhs)<size(rhs); }
const std::string &), mais depuis C++14 l'usage de auto est autorisé ce qui facilite la réutilisation du code si nous décidions de choisir un autre type pour l'instanciation de la fonction template.
const auto inv_cmp= [&](const auto &lhs, const auto &rhs) { return lhs>rhs; };
<functional>.
>sur ses deux paramètres.
<. Complétez donc la fonction test_min_max() avec les affichages de nouvelles invocations des fonctions std::min<T>() et std::max<T>() appliquées à vos variables de type entier et de type réel, en utilisant la variable inv_cmp comme troisième paramètre effectif.
[&](const auto &lhs, const auto &rhs) { return lhs[pos]<rhs[pos]; }
template<typename T> inline T avg(const T &a, const T &b) { return (a+b)/2; }
inline std::string avg(const std::string &a, const std::string &b) { const int sza=int(size(a)); const int szb=int(size(b)); const int sz=avg(sza, szb); std::string result; for(int i=0; int(size(result))<sz; ++i) { if(i<sza) { result+=a[i]; } if(i<szb) { result+=b[i]; } } return result; }
template<typename T1, typename T2> inline auto avg(const T1 &a, const T2 &b) { return (a+b)/2; }
template<typename T> inline auto avg(const T &a, const std::string &b) { using std::to_string; return avg(to_string(a), b); }
template<typename T> inline auto avg(const std::string &a, const T &b) { using std::to_string; return avg(a, to_string(b)); }
<string>pour produire une chaîne à partir de leur paramètre template qui n'en est pas une.
for(const auto &element: container) { std::cout << element << ' '; } std::cout << '\n';
<cctype>transforme une lettre minuscule en majuscule).
// WARNING: a range-for loop should have been used in real C++ code! for(auto it=cbegin(str_vector); it!=cend(str_vector); ++it) { std::cout << *it << ' '; } std::cout << '\n';
<iterator>expose notamment des fonctions std::begin() et std::end() ainsi que leurs variantes std::cbegin() et std::cend().
// WARNING: a range-for loop should have been used in real C++ code! const std::string *cbegin_ptr=&str_vector[0]; // address of the first element const std::string *cend_ptr=cbegin_ptr+size(str_vector); // past the last element for(const std::string *ptr=cbegin_ptr; ptr!=cend_ptr; ++ptr) { std::cout << *ptr << ' '; } std::cout << '\n';
myns::MyType container= ... something ... ; using std::cend; // in case myns::cend() does not exist for myns::MyType const auto it=cend(container); // may use myns::cend() (if available) // or, by default, std::cend() (if suitable)
template<typename Container> inline auto do_something(const Container &container, ...) { using std::cbegin; // fallback to std::cbegin() if nothing better exists using std::cend; // fallback to std::cend() if nothing better exists std::for_each(cbegin(container), cend(container), // use a standard algorithm ... // on this container ... ...); ... }
str_vector.emplace(cbegin(str_vector)+3, "NEW"); str_vector.erase(cbegin(str_vector)+2);
const auto show= [&](const auto &seq) { for(const auto &e: seq) { std::cout << e << ' '; } std::cout << '\n'; };
<algorithm>.
if(const auto pos=std::find(begin(str_vector), end(str_vector), "something"); pos!=end(str_vector)) { *pos="ELSE"; }
if(container.empty()) { ... } const int sz=int(container.size()); const auto it1=container.cbegin(); const auto it2=container.end();
if(empty(container)) { ... } const int sz=int(size(container)); const auto it1=cbegin(container); const auto it2=end(container);
<algorithm>et
<numeric>exposent une multitude de fonctions génériques qui s'appliquent à de telles séquences désignées par des itérateurs.
const auto min1=std::min_element(cbegin(str_vector), cend(str_vector)); const auto max1=std::max_element(cbegin(str_vector), cend(str_vector));
const auto no_case_cmp= [&](const auto &lhs, const auto &rhs) { return std::lexicographical_compare( cbegin(lhs), cend(lhs), cbegin(rhs), cend(rhs), [&](const auto &lhs_c, const auto &rhs_c) { return std::tolower(lhs_c)<std::tolower(rhs_c); }); };
template<typename Iter> inline auto seq_avg(Iter begin_it, Iter end_it) { if(begin_it==end_it) { throw std::runtime_error{"no average on an empty sequence"}; } auto sum=*begin_it; int count=1; std::for_each(begin_it+1, end_it, [&](const auto &elem) { sum+=elem; ++count; }); return sum/count; }
<algorithm>, lorsque aucune subtilité dans le parcours de la séquence ne justifie sa rédaction explicite (cela rejoint ce qui a été exposé à la fin de cette section).
[&](auto &lhs, const auto &rhs) { lhs+=rhs; }
[&](const auto &lhs, const auto &rhs) { return lhs/rhs; }
[&](const auto &lhs, const auto &rhs) { std::string result; for(int i=0; i<int(size(lhs)); i+=rhs) { result+=lhs[i]; } return result; }
using Point2D = std::array<double, 2>; const std::vector<Point2D> points={{1.2, 3.4}, {5.6, 7.8}, {9.0, 1.2}};
[&](auto &lhs, const auto &rhs) { lhs[0]+=rhs[0]; lhs[1]+=rhs[1]; }
[&](const auto &lhs, const auto &rhs) { return Point2D{lhs[0]/rhs, lhs[1]/rhs}; }
<algorithm>et
<numeric>partout où ils s'appliquent.
<array>, a été rencontré très tôt dans cet enseignement.
<vector>.
const int amount=guess_number_of_elements(); std::vector<Element> v; v.reserve(amount); // pre-allocation for at least amount elements for(int i=0; i<amount; ++i) { v.emplace_back(obtain_an_element()); }
capacity: 10 (storage is allocated for 10 elements) size: 8 (only 8 elements are stored, 2 slots left in storage) content: [ A | B | C | D | E | F | G | H | . | . ]L'ajout d'un nouvel élément à la fin par v.emplace_back(X) reviendrait :
capacity: 10 (storage is allocated for 10 elements) size: 9 (only 9 elements are stored, 1 slot left in storage) content: [ A | B | C | D | E | F | G | H | X | . ]
capacity: 10 (storage is allocated for 10 elements) size: 7 (only 7 elements are stored, 3 slots left in storage) content: [ A | B | C | D | E | F | G | . | . | . ]
capacity: 10 (storage is allocated for 10 elements) size: 8 (only 8 elements are stored, 2 slots left in storage) content: [ A | B | C | D | E | F | G | H | . | . ]L'ajout d'un nouvel élément par v.emplace(cbegin(v)+2, X) reviendrait :
capacity: 10 (storage is allocated for 10 elements) size: 9 (only 9 elements are stored, 1 slot left in storage) content: [ A | B | X | C | D | E | F | G | H | . ]
capacity: 10 (storage is allocated for 10 elements) size: 7 (only 7 elements are stored, 3 slots left in storage) content: [ A | B | D | E | F | G | H | . | . | . ]
v.erase(cbegin(v)+idx);
v[idx]=std::move(v.back()); // move last to idx v.pop_back(); // get rid of (moved-from) last
*(begin(v)+idx)=std::move(*(end(v)-1)); // move last to idx
<deque>, visent à donner l'illusion de tableaux dynamiques pour lesquels les manipulations seraient optimales à la fois du côté de leur début (front) et de leur fin (back).
<list>, représente une liste chaînée qui désigne une succession de nœuds.
<set>, représente un ensemble d'éléments qui sont uniques du point de vue du critère de comparaison de leur type (leur opérateur <).
std::set<int> s; for(int i=0; i<10; ++i) { const int value=((i+2)*(i+5))%11; if(const auto [iter, inserted]=s.insert(value); inserted) { std::cout << value << " -->"; for(const auto &elem: s) { std::cout << ' ' << elem; } std::cout << '\n'; } else { std::cout << *iter << " is already present\n"; } } if(const auto iter=s.find(7); iter!=cend(s)) { std::cout << *iter << " found, will be removed\n"; s.erase(iter); } std::cout << "final set -->"; for(const auto &elem: s) { std::cout << ' ' << elem; } std::cout << '\n';
10 --> 10 7 --> 7 10 6 --> 6 7 10 7 is already present 10 is already present 4 --> 4 6 7 10 0 --> 0 4 6 7 10 9 --> 0 4 6 7 9 10 9 is already present 0 is already present 7 found, will be removed final set --> 0 4 6 9 10Nous constatons que la fonction membre d'insertion insert() renvoie deux résultats (std::pair<T1, T2>, c'est-à-dire std::tuple<...> à deux membres) :
<map>, peut être vu comme la généralisation du type std::set<T> : il utilise le même procédé pour organiser des clefs qui sont associées à des données✍
std::map<std::string, int> m; m["unan"]=1; m["daou"]=2; m["tri"]=3; m["pevar"]=4; m["pemp"]=5; m["c'hwec'h"]=6; m["seizh"]=7; m["eizh"]=8; m["nav"]=9; m["dek"]=10; for(const auto &[key, value]: m) { std::cout << key << " --> " << value << '\n'; } std::cout << "pevar means " << m["pevar"] << '\n';
c'hwec'h --> 6 daou --> 2 dek --> 10 eizh --> 8 nav --> 9 pemp --> 5 pevar --> 4 seizh --> 7 tri --> 3 unan --> 1 pevar means 4Nous constatons que l'opérateur [] propose un moyen confortable de réaliser des consultations et des ajouts (il existe toutefois les fonctions membres find(), emplace() et erase() qui offrent un contrôle plus fin).
template<typename Container> struct Sorted { Container content; };
template<typename Container> inline auto // typename Container::const_iterator cbegin(const Sorted<Container> &sorted) { return cbegin(sorted.content); }
template<typename Container> inline auto // element position or end (typename Container::const_iterator) find(const Sorted<Container> &sorted, const typename Container::value_type &value) { const auto it=std::lower_bound(cbegin(sorted), cend(sorted), value); return (it==cend(sorted))||(value<*it) ? cend(sorted) : it; }
<algorithm>, pour effectuer une recherche dichotomique (donc efficace) sur une séquence supposée triée.
template<typename Container> inline auto // insert position (typename Container::const_iterator) insert(Sorted<Container> &sorted, typename Container::value_type value) { const auto it=std::lower_bound(cbegin(sorted), cend(sorted), value); return sorted.content.emplace(it, std::move(value)); }
template<typename Container> inline auto // next element position or end (typename Container::const_iterator) erase(Sorted<Container> &sorted, typename Container::const_iterator it) { return sorted.content.erase(it); }
s4prc::Sorted<std::vector<int>> sv; s4prc::Sorted<std::deque<int>> sd; s4prc::Sorted<std::list<int>> sl;
//----------------------------------------------------------------------------
#ifndef S4PRC_AVERAGE_HPP #define S4PRC_AVERAGE_HPP
#include <string> #include <algorithm> #include <stdexcept>
namespace s4prc {
#if 0 // both arguments have the same type
template<typename T> inline T avg(const T &a, const T &b) { return (a+b)/2; }
#else
template<typename T1, typename T2> inline auto avg(const T1 &a, const T2 &b) { return (a+b)/2; }
#endif
inline std::string avg(const std::string &a, const std::string &b) { const int sza=int(size(a)); const int szb=int(size(b)); const int sz=avg(sza, szb); std::string result; for(int i=0; int(size(result))<sz; ++i) { if(i<sza) { result+=a[i]; } if(i<szb) { result+=b[i]; } } return result; }
template<typename T> inline auto avg(const T &a, const std::string &b) { using std::to_string; return avg(to_string(a), b); }
template<typename T> inline auto avg(const std::string &a, const T &b) { using std::to_string; return avg(a, to_string(b)); }
#if 0 // hardcoded usage of += and /
template<typename Iter> inline auto seq_avg(Iter begin_it, Iter end_it) { if(begin_it==end_it) { throw std::runtime_error{"no average on an empty sequence"}; } auto sum=*begin_it; int count=1; std::for_each(begin_it+1, end_it, [&](const auto &elem) { sum+=elem; ++count; }); return sum/count; }
#else
template<typename Iter, typename AccumOper, typename DivideOper> inline auto seq_avg(Iter begin_it, Iter end_it, AccumOper accum, DivideOper divide) { if(begin_it==end_it) { throw std::runtime_error{"no average on an empty sequence"}; } auto sum=*begin_it; int count=1; std::for_each(begin_it+1, end_it, [&](const auto &elem) { accum(sum, elem); ++count; }); return divide(sum, count); }
template<typename Iter, typename DivideOper> inline auto seq_avg(Iter begin_it, Iter end_it, DivideOper divide) { return seq_avg(begin_it, end_it, [&](auto &lhs, const auto &rhs) { lhs+=rhs; }, divide); }
template<typename Iter> inline auto seq_avg(Iter begin_it, Iter end_it) { return seq_avg(begin_it, end_it, [&](const auto &lhs, const auto &rhs) { return lhs/rhs; }); }
#endif
} // namespace s4prc
#endif // S4PRC_AVERAGE_HPP
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
#ifndef S4PRC_SORTED_HPP #define S4PRC_SORTED_HPP
#include <algorithm>
namespace s4prc {
template<typename Container> struct Sorted { Container content; };
template<typename Container> inline auto // typename Container::const_iterator cbegin(const Sorted<Container> &sorted) { return cbegin(sorted.content); }
template<typename Container> inline auto // typename Container::const_iterator begin(const Sorted<Container> &sorted) { return cbegin(sorted); }
template<typename Container> inline auto // typename Container::const_iterator cend(const Sorted<Container> &sorted) { return cend(sorted.content); }
template<typename Container> inline auto // typename Container::const_iterator end(const Sorted<Container> &sorted) { return cend(sorted); }
template<typename Container> inline auto // element position or end (typename Container::const_iterator) find(const Sorted<Container> &sorted, const typename Container::value_type &value) { const auto it=std::lower_bound(cbegin(sorted), cend(sorted), value); return (it==cend(sorted))||(value<*it) ? cend(sorted) : it; }
template<typename Container> inline auto // insert position (typename Container::const_iterator) insert(Sorted<Container> &sorted, typename Container::value_type value) { const auto it=std::lower_bound(cbegin(sorted), cend(sorted), value); return sorted.content.emplace(it, std::move(value)); }
template<typename Container> inline auto // next element position or end (typename Container::const_iterator) erase(Sorted<Container> &sorted, typename Container::const_iterator it) { return sorted.content.erase(it); }
} // namespace s4prc
#endif // S4PRC_SORTED_HPP
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
#include "average.hpp" #include "sorted.hpp" #include <iostream> #include <algorithm> #include <cctype> #include <array> #include <vector> #include <deque> #include <list> #include <set> #include <map> #include <utility>
void test_min_max() { std::cout << "\n~~~~ " << __func__ << "() ~~~~\n"; using std::min; using std::max; const int i1=12, i2=34; std::cout << "min(" << i1 << ", " << i2 << ")=" << min(i1, i2) << '\n'; std::cout << "max(" << i1 << ", " << i2 << ")=" << max(i1, i2) << '\n'; const double d1=12.34, d2=34.56; std::cout << "min(" << d1 << ", " << d2 << ")=" << min(d1, d2) << '\n'; std::cout << "max(" << d1 << ", " << d2 << ")=" << max(d1, d2) << '\n'; const std::string s1="Hello", s2="Antidisestablishmentarianism"; std::cout << "min(" << s1 << ", " << s2 << ")=" << min(s1, s2) << '\n'; std::cout << "max(" << s1 << ", " << s2 << ")=" << max(s1, s2) << '\n'; std::cout << "--------\n"; std::cout << "size !!! min(" << s1 << ", " << s2 << ")=" << min(s1, s2, [&](const auto &lhs, const auto &rhs) { return size(lhs)<size(rhs); }) << '\n'; const auto inv_cmp= [&](const auto &lhs, const auto &rhs) { return lhs>rhs; }; std::cout << "inverted !!! min(" << i1 << ", " << i2 << ")=" << min(i1, i2, inv_cmp) << '\n'; std::cout << "inverted !!! max(" << d1 << ", " << d2 << ")=" << max(d1, d2, inv_cmp) << '\n'; const int min_size=int(std::min(size(s1), size(s2))); for(int pos=0; pos<min_size; ++pos) { std::cout << "pos " << pos << " !!! min(" << s1 << ", " << s2 << ")=" << min(s1, s2, [&](const auto &lhs, const auto &rhs) { return lhs[pos]<rhs[pos]; }) << '\n'; } }
void test_average() { std::cout << "\n~~~~ " << __func__ << "() ~~~~\n"; using s4prc::avg; const int i1=12, i2=34; std::cout << "avg(" << i1 << ", " << i2 << ")=" << avg(i1, i2) << '\n'; std::cout << "avg(" << i2 << ", " << i1 << ")=" << avg(i2, i1) << '\n'; const double d1=12.34, d2=34.56; std::cout << "avg(" << d1 << ", " << d2 << ")=" << avg(d1, d2) << '\n'; std::cout << "avg(" << d2 << ", " << d1 << ")=" << avg(d2, d1) << '\n'; std::cout << "--------\n"; const std::string s1="Hello", s2="Antidisestablishmentarianism"; std::cout << "avg(" << s1 << ", " << s2 << ")=" << avg(s1, s2) << '\n'; std::cout << "avg(" << s2 << ", " << s1 << ")=" << avg(s2, s1) << '\n'; std::cout << "--------\n"; std::cout << "avg(" << d1 << ", " << i2 << ")=" << avg(d1, i2) << '\n'; std::cout << "avg(" << i1 << ", " << d2 << ")=" << avg(i1, d2) << '\n'; std::cout << "avg(" << i1 << ", " << s2 << ")=" << avg(i1, s2) << '\n'; std::cout << "avg(" << s1 << ", " << d2 << ")=" << avg(s1, d2) << '\n'; }
void test_range_for() { std::cout << "\n~~~~ " << __func__ << "() ~~~~\n"; std::array<std::string, 7> str_array={"here", "are", "many", "strings", "in", "a", "std::array<T,N>"}; std::vector<std::string> str_vector={"here", "are", "many", "strings", "in", "a", "std::vector<T>"}; for(const auto &elem: str_array) { std::cout << elem << ' '; } std::cout << '\n'; for(const auto &elem: str_vector) { std::cout << elem << ' '; } std::cout << '\n'; std::cout << "--------\n"; for(const auto &elem: {2, 3, 5, 7, 11, 13, 17, 19, 23, 29}) { std::cout << elem << ' '; } std::cout << '\n'; for(const auto &elem: {"we", "can", "enumerate", "anything"}) { std::cout << elem << ' '; } std::cout << '\n'; std::cout << "--------\n"; for(auto &elem: str_vector[0]) { elem=char(std::toupper(elem)); } std::cout << str_vector[0] << '\n'; }
void test_iter() { std::cout << "\n~~~~ " << __func__ << "() ~~~~\n"; std::vector<std::string> str_vector={"here", "are", "many", "strings", "in", "a", "std::vector<T>"}; // WARNING: a range-for loop should have been used in real C++ code! for(auto it=cbegin(str_vector); it!=cend(str_vector); ++it) { std::cout << *it << ' '; } std::cout << '\n'; const auto show= [&](const auto &seq) { for(const auto &elem: seq) { std::cout << elem << ' '; } std::cout << '\n'; }; str_vector.emplace(cbegin(str_vector)+3, "NEW"); show(str_vector); str_vector.erase(cbegin(str_vector)+2); show(str_vector); if(const auto pos=std::find(begin(str_vector), end(str_vector), "a"); pos!=end(str_vector)) { *pos="THIS"; } show(str_vector); std::cout << "--------\n"; const auto min1=std::min_element(cbegin(str_vector), cend(str_vector)); const auto max1=std::max_element(cbegin(str_vector), cend(str_vector)); std::cout << "*min1: " << *min1 << " *max1: " << *max1 << '\n'; const auto no_case_cmp= [&](const auto &lhs, const auto &rhs) { return std::lexicographical_compare( cbegin(lhs), cend(lhs), cbegin(rhs), cend(rhs), [&](const auto &lhs_c, const auto &rhs_c) { return std::tolower(lhs_c)<std::tolower(rhs_c); }); }; const auto min2=std::min_element(cbegin(str_vector), cend(str_vector), no_case_cmp); const auto max2=std::max_element(cbegin(str_vector), cend(str_vector), no_case_cmp); std::cout << "*min2: " << *min2 << " *max2: " << *max2 << '\n'; std::cout << "--------\n"; const std::vector<double> values={1.0, 2.0, 3.0, 4.0, 0.5, 0.6, 0.7, 0.8}; const auto full_avg=s4prc::seq_avg(cbegin(values), cend(values)); const auto partial_avg=s4prc::seq_avg(cbegin(values)+2, cend(values)-2); std::cout << "full_avg: " << full_avg << '\n'; std::cout << "partial_avg: " << partial_avg << '\n'; const auto str_avg=s4prc::seq_avg(cbegin(str_vector), cend(str_vector), [&](const auto &lhs, const auto &rhs) { std::string result; for(int i=0; i<int(size(lhs)); i+=rhs) { result+=lhs[i]; } return result; }); std::cout << "str_avg: " << str_avg << '\n'; using Point2D = std::array<double, 2>; const std::vector<Point2D> points={{1.2, 3.4}, {5.6, 7.8}, {9.0, 1.2}}; const auto point_avg=s4prc::seq_avg(cbegin(points),cend(points), [&](auto &lhs, const auto &rhs) { lhs[0]+=rhs[0]; lhs[1]+=rhs[1]; }, [&](const auto &lhs, const auto &rhs) { return Point2D{lhs[0]/rhs, lhs[1]/rhs}; }); std::cout << "point_avg: "; show(point_avg); }
void test_set() { std::cout << "\n~~~~ " << __func__ << "() ~~~~\n"; std::set<int> s; for(int i=0; i<10; ++i) { const int value=((i+2)*(i+5))%11; if(const auto [iter, inserted]=s.insert(value); inserted) { std::cout << value << " -->"; for(const auto &elem: s) { std::cout << ' ' << elem; } std::cout << '\n'; } else { std::cout << *iter << " is already present\n"; } } if(const auto iter=s.find(7); iter!=cend(s)) { std::cout << *iter << " found, will be removed\n"; s.erase(iter); } std::cout << "final set -->"; for(const auto &elem: s) { std::cout << ' ' << elem; } std::cout << '\n'; }
void test_map() { std::cout << "\n~~~~ " << __func__ << "() ~~~~\n"; std::map<std::string, int> m; m["unan"]=1; m["daou"]=2; m["tri"]=3; m["pevar"]=4; m["pemp"]=5; m["c'hwec'h"]=6; m["seizh"]=7; m["eizh"]=8; m["nav"]=9; m["dek"]=10; for(const auto &[key, value]: m) { std::cout << key << " --> " << value << '\n'; } std::cout << "pevar means " << m["pevar"] << '\n'; }
void test_sorted() { std::cout << "\n~~~~ " << __func__ << "() ~~~~\n"; std::vector<int> v; s4prc::Sorted<std::vector<int>> sv; s4prc::Sorted<std::deque<int>> sd; s4prc::Sorted<std::list<int>> sl; for(int i=0; i<20; ++i) { const int value=((i+2)*(i+5))%11; v.emplace_back(value); insert(sv, value); insert(sd, value); insert(sl, value); } const auto show= [&](const auto &title, const auto &ctnr) { std::cout << title << ':'; for(const auto &elem: ctnr) { std::cout << ' ' << elem; } std::cout << '\n'; }; show(" values", v); show("sorted vector", sv); show(" sorted deque", sd); show(" sorted list", sl); const auto use_and_show= [&](const auto &title, const auto &value, auto &ctnr) { if(const auto it=find(ctnr, value); it!=cend(ctnr)) { erase(ctnr, it); } show(title, ctnr); }; for(const auto &value: v) { use_and_show("sorted vector", value, sv); use_and_show(" sorted deque", value, sd); use_and_show(" sorted list", value, sl); } }
int main() { test_min_max(); test_average(); test_range_for(); test_iter(); test_set(); test_map(); test_sorted(); return 0; }
//----------------------------------------------------------------------------