La programmation en C++ moderne

Apprenez la programmation de zéro jusqu'à l'infini !

a marqué ce sujet comme résolu.

§ 11 les lambdas

Intro: en fait, les fonctions ne plaisent pas trop car elles sont plus compliquées à inliner de façon sûre, et du coup ce sont les foncteurs qui sont, qui étaient, préférés. Très bonnes perfs, flexibles. Sauf que: pour exprimer un prédicat, on se cogne facilement jusqu’à 7 lignes de bruit. Autant dire que personne n’utilisait find_if()

lmghs

Je ne suis pas très adepte de l’introduction par l’intérêt historique ; on ne s’adresse pas ici à des développeurs qui travaillent en C++03, mais à des débutants en C++. Ok pour leur dire que c’est plus pratique qu’une fonction ou qu’un foncteur (même si je doute fort de l’intérêt d’expliquer les foncteurs avant d’introduire les lambdas justement), mais l’argumentaire que tu développe aurait plutôt sa place dans une formation de remise à niveau en entreprise…

Nouveauté C++17 : les décompositions | Nouveautés du C++14 | FYS : une bibliothèque C++ dans le domaine public

+1 -0

Salut les amis,

Au programme, la complétion de la partie sur Qt Creator, avec MinGW à jour, ainsi que les corrections soulevées par @gbdivers. Pour tes remarques @lmghs, j’y travaille. Pour celles des lambdas, pas grand chose à dire. Pour les autres sur le chapitre sur les conteneurs, tu peux déjà jeter un œil. Pour le chapitre sur la gestion des erreurs, je ne sais pas quoi dire. Il est tellement compliqué à écrire que j’ai presque envie de le saborder parfois.

Merci d’avance pour vos commentaires.

PS : hier, cela faisait déjà trois ans que ce topic existe, et donc ce tutoriel. Que de chemin accomplit et de réécriture. Mais le bout du tunnel est là pour une grosse partie de la section I. J’espère une publication pour le 1er août. :)

Édité par informaticienzero

§ 11 les lambdas

Intro: en fait, les fonctions ne plaisent pas trop car elles sont plus compliquées à inliner de façon sûre, et du coup ce sont les foncteurs qui sont, qui étaient, préférés. Très bonnes perfs, flexibles. Sauf que: pour exprimer un prédicat, on se cogne facilement jusqu’à 7 lignes de bruit. Autant dire que personne n’utilisait find_if()

lmghs

Je ne suis pas très adepte de l’introduction par l’intérêt historique ; on ne s’adresse pas ici à des développeurs qui travaillent en C++03, mais à des débutants en C++. Ok pour leur dire que c’est plus pratique qu’une fonction ou qu’un foncteur (même si je doute fort de l’intérêt d’expliquer les foncteurs avant d’introduire les lambdas justement), mais l’argumentaire que tu développe aurait plutôt sa place dans une formation de remise à niveau en entreprise…

germinolegrand

Je ne suis pas très fan non plus de présenter ces détails. Maintenant, la raison qui avait été donnée dans le tuto n’est pas la bonne. La bonne, je la donne pour information. Côté tuto, on peut se contenter de "pour des raisons de praticité et de rapidité" — et éventuellement (même pas nécessaire) renvoyer à plus tard pour la raison pour laquelle ce peut être plus rapide.

+0 -0

PS : hier, cela faisait déjà trois ans que ce topic existe, et donc ce tutoriel. Que de chemin accomplit et de réécriture. Mais le bout du tunnel est là pour une grosse partie de la section I. J’espère une publication pour le 1er août. :)

informaticienzero

Un ami que je connais bien m’a dit que c’était un gros boulot d’écrire un cours…

Je voulais continuer de relire la suite. Tu veux que je le fasse maintenant ou après avoir intégrer les modifications de luc ?

Pour poser des questions ou simplement discuter informatique, vous pouvez rejoindre le discord NaN.

+0 -0

Je voulais continuer de relire la suite. Tu veux que je le fasse maintenant ou après avoir intégrer les modifications de luc ?

gbdivers

Tu peux continuer maintenant, si tu veux. Merci pour tes retours. :)

Je ne suis pas très fan non plus de présenter ces détails. Maintenant, la raison qui avait été donnée dans le tuto n’est pas la bonne. La bonne, je la donne pour information. Côté tuto, on peut se contenter de "pour des raisons de praticité et de rapidité" — et éventuellement (même pas nécessaire) renvoyer à plus tard pour la raison pour laquelle ce peut être plus rapide.

lmghs

Je vais corriger ça sans entrer dans les détails.

j’aime bien l’idée d’utiliser l’exemple de la vérification des entrées comme fil rouge entre les chapitres. Au moins, c’est un vrai fil rouge utile.

Plus on avance dans la lecture, moins j’ai de remarque à faire sur la clareté des explications. C’est plus des remarques de chipotage que des vrais critiques que je fais.

Des boucles qui se répètent, répètent, répètent…

  • "Image originale tirée du…" : problème avec le lien dans la legende de l’image

  • "int compteur {};" -> "int compteur { 0 };" : j’aime ce qui est explicite

  • "Tant que compteur est différent de 10" : le code utilise "<" donc dire "est strictement inférieur".

  • "char entree { '?' }; int compteur { 0 };" : idem pour expliciter

  • "5kg […] 10kg" à vérifier au niveau typographie, mais je pense qu’on doit mettre un espace (insécable) entre le chiffre et les unités.

  • "Nous pouvons même effectuer plusieurs opérations en même temps." : avec une restriction : les variables "compteur" et "compteur_inverse" doivent avoir le même type.

  • "unsigned int facteur_gauche { 1u };" : je ne suis pas fan de l’utilisation des unsigned. Juste avant, tu présentes un for avec un int et une décrémentation. Le lecteur pourrait être tenté des faire des opérations avec un unsigned.

Mon avis : osef unsigned. Juste size_t dans le cas particulier des collections de la lib standard. Sinon que des int.

  • "Ce nom est tellement court et explicite qu’il est pour ainsi dire devenu une convention de nommage en C++." : peut etre préciser que pour un vrai code, on préfère avoir des noms de variables plus explicite ?

  • "int i {0}" -> "int i { 0 }"

  • "C++ nous offre trois façons de boucler : while, do while et for" : tu as oublié de parler de goto :D Plus sérieusement, peut etre dire qu’il y a plusiers syntaxes pour for ?

Au tableau !

  • "std::vector</* type des éléments du tableau */> identifiant {};" : je préfère la version/recommendation du C++14 ou 17 (je ne sais plus ) pour les listes de valeurs dans un tableau : "vector<T> v = {};". De memoire, ca force a utiliser les valeurs avec initializer_list et pas les autres constructeurs. Donc pas d’ambiguité.

  • "std::vector<int> const tableaudeint { 1, 2, 3, 4, 5 };" -> "std::vector<int> const tableaudeint = { 1, 2, 3, 4, 5 };"

  • "C’est un héritage du C que je ne vais pas détailler car il demande des connaissances que vous n’avez pas encore." : tu peux peut être simplement dire que l’indice correspond au nombre de déplacement à partir du premier élément ? 0 déplacement = le premier élément. i=1 = un déplacement = on est sur le deuxième élément. Etc. (Avec un schema pour bien comprendre ? Cette histoire d’indice 0 est une erreur classique)

  • J’ai l’impression que ce chapitre est plus long que les autres. Avoir des chapitres de même longueur (et pas trop long) doit aider à ne pas se décourager pendant la lecture (impression d’avancer). Peut etre séparer en 2 ?

  • "donc int peut ne pas convenir pour stocker la taille d’un tableau" : mouais, bof. Utiliser sizet au lieu de int fait qu’on peut indexer "que" 4G élément au lieu de 2G. Qui a réellement besoin d’avoir sizet au lieu de int ? A mon sens, dire que ça utilise size_t plutot que int pour des raisons historiques est suffisant.

  • "Par contre, C++ définit un mot-clef extrêmement utile qui s’appelle auto" : ca me semble maladroit d’introduire auto ici et avec aussi peu d’explication sur la déduction de type.

Surtout que l’utilité de auto dans ce cas est discutable. Pourquoi écrire "auto" au lieu de "size_t" ? Et si on veut utiliser une boucle for, on va peut etre vouloir ecrire :

for (auto i { 0 }; i < v.size(); ++i) ...
  • Parler de la version libre de "size" ? https://en.cppreference.com/w/cpp/iterator/size

  • "Depuis l’arrivée de C++11, il existe une variante de la boucle for" : penser a dire dans la partie sur les for qu’il existe d’autres synthaxes.

  • "Fonction bien utile, empty permet de vérifier si un tableau est vide" : idem, parler de std::empty

  • "for (auto const chaine : tableaudestring)" : le problème de ne pas encore avoir vu les références… peut etre donner la syntaxe for(auto&& ... uniquement ? (En disant simplement que cela crée une reference universelle, ce qui est hors programme)

  • "Les assertions" : pourquoi là et maintenant ?

  • "std::array — Le tableau statique" : peut être mettre dans un autre chapitre, pour alleger celui la ?

std::string — Un type qui cache bien son jeu

  • "assert(phrase.length() == phrase.size());" : pas sur de l’utilité de parler de length

  • "Les deux fonctions front et back permettent d’ailleurs d’accéder, respectivement, au premier et au dernier caractère de la chaîne." : pourquoi parler de front et back que pour string ? Et pas avant ? Ou pas du tout ?

Pour poser des questions ou simplement discuter informatique, vous pouvez rejoindre le discord NaN.

+0 -0

Merci pour tes retours.

  • "std::vector</* type des éléments du tableau */> identifiant {};" : je préfère la version/recommendation du C++14 ou 17 (je ne sais plus ) pour les listes de valeurs dans un tableau : "vector

    <T>

    v = {};". De memoire, ca force a utiliser les valeurs avec initializer_list et pas les autres constructeurs. Donc pas d’ambiguité.
gbdivers

Je n’ai pas vu cette recommandation. Elle tient toujours en C++17 ?

  • J’ai l’impression que ce chapitre est plus long que les autres. Avoir des chapitres de même longueur (et pas trop long) doit aider à ne pas se décourager pendant la lecture (impression d’avancer). Peut etre séparer en 2 ?
gbdivers

À voir en effet.

  • "donc int peut ne pas convenir pour stocker la taille d’un tableau" : mouais, bof. Utiliser sizet au lieu de int fait qu’on peut indexer "que" 4G élément au lieu de 2G. Qui a réellement besoin d’avoir sizet au lieu de int ? A mon sens, dire que ça utilise size_t plutot que int pour des raisons historiques est suffisant.

  • "Par contre, C++ définit un mot-clef extrêmement utile qui s’appelle auto" : ca me semble maladroit d’introduire auto ici et avec aussi peu d’explication sur la déduction de type.

gbdivers

Du coup je vais retravailler cette section. Mais toi, quand verrais-tu l’introduction de auto, et quelles explications rajouterais-tu ? Je m’étais basé sur cette réflexion de @lmghs.

Ce type c’est std::size_t. Et c’est ce type que nous allons utiliser.

C’est une super occasion pour sortir auto, façon de dire: "dans le doute, avec auto votre variable sera du bon type" Source:lmghs

gbdivers

Oui, je vais écrire ça.

  • "for (auto const chaine : tableaudestring)" : le problème de ne pas encore avoir vu les références… peut etre donner la syntaxe for(auto&& ... uniquement ? (En disant simplement que cela crée une reference universelle, ce qui est hors programme)
gbdivers

À voir. Surtout que ce problème est résolu dans le chapitre suivant.

  • "Les assertions" : pourquoi là et maintenant ?
gbdivers

Ça me semblait cohérent à l’époque, maintenant je me demande en effet si cette section à sa place dans ce chapitre. Je vais y réfléchir.

  • "Les deux fonctions front et back permettent d’ailleurs d’accéder, respectivement, au premier et au dernier caractère de la chaîne." : pourquoi parler de front et back que pour string ? Et pas avant ? Ou pas du tout ?
gbdivers

Avant à quel moment ?

Salut à tous,

Finalement, j’ai décidé de ne pas parler ni de la valeur par défaut, ni de unsigned int. J’ai également pris plusieurs remarques de @gbdivers en compte. Le chapitre sur les tableaux est par contre en cours de modification, donc vous pouvez me faire des retours dessus, mais il sera peut-être amené à bouger.

Merci d’avance pour vos commentaires.

Édité par informaticienzero

Je n’ai pas vu cette recommandation. Elle tient toujours en C++17 ?

Je n’étais plus sûr de ce que je racontais. J’ai vérifié avec jolinknoir, tout est expliqué là : https://zestedesavoir.com/forums/sujet/5876/debuter-en-c/#p107110

En fait, en C++14, la syntaxe auto x = { ... } est interprété systématiquement comme un initializer_list. Par analogie, je trouve que c’est pas mal de l’utiliser aussi en dehors de auto pour indiquer une liste de valeurs.

Mais c’est une recommendation personnelle, rien d’officiel.

Mais toi, quand verrais-tu l’introduction de auto, et quelles explications rajouterais-tu ? Je m’étais basé sur cette réflexion de @lmghs.

Quand on regarde les autres langages, beaucoup explique les variable sans que le type soit explicite, mais pour les langages qui propose les 2 approches, comme le C++. (J’ai vu ca en particulier dans un cours Rust).

De plus, si on décide de présenter systématiquement l’initialisation, on a toujours une redondance des types.

Du coup, a mon avis, il faut présenter auto par défaut et le typage explicite en option.

Oui, c’est violent.

Sinon, probablement juste aprés la partie sur les variables.

Avant à quel moment ?

Pour vector et array.

Pour poser des questions ou simplement discuter informatique, vous pouvez rejoindre le discord NaN.

+0 -0

Salut à tous,

Alors, suivant les recommandations de @gbdivers, j’ai modifié le chapitre sur les variables pour introduire auto à ce niveau. Je modifierai plusieurs chapitres pour distiller son utilisation, conjointement avec le typage explicite.

J’ai également modifié le chapitre sur les tableaux pour prendre en compte ces modifications, ainsi que pour parler de std::empty et std::size. Il me reste néanmoins beaucoup de travail, avec notamment la suppression de la partie sur les assertions pour la mettre ailleurs, modifier des explications dans le chapitre sur les fonctions.

Merci d’avance pour vos commentaires.

Édité par informaticienzero

Salut à tous,

Au programme, j’ai un peu amélioré le chapitre sur la gestion des erreurs, j’ai supprimé toutes les initialisations par défaut pour les remplacer par du plus explicite, j’ai mis à jour des explications dans le chapitre sur les fonctions et réécrits quelques tournures et exemples par-ci par-là.

Merci d’avance pour vos commentaires.

Édité par informaticienzero

Salut à tous.

Au programme, une grande réécriture du chapitre sur les erreurs. J’ai supprimé la section sur les exceptions, parce que j’ai estimé que ce n’était pas le bon moment et qu’elles n’apporteraient rien à ce stade.

Je corrige donc les chapitres suivants en conséquence. J’ai aussi décidé d’ajouter une nouvelle partie au T.P sur les entrées. :)

Merci d’avance pour vos commentaires.

PS : vive l’Union Européenne et la suppression des frais de roaming ! Vive la 4G à l’étranger comme en France.

Édité par informaticienzero

Cette réponse a aidé l’auteur du sujet

Desole, j’ai pris une petite pause. Je reprends la lecture.

Découpons du code — Les fonctions

Les éléments de base d’une fonction

"clear, push_back, length" : tu ne parles pas de la difference entre fonction libre et membre ? Si tu utilises comme exemple des fonctions membres, qui ont une syntaxe specifique pour etre appellee, cela peut porter a confusion. Peut etre limiter aux fonctions libres dans les exemples.

Retour sur une fonction bien connue

pas sur de cette partie. C’est une bonne chose de commencer un chapitre en faisant le lien vers ce qui est deja connu, pour faciliter l’assimilation. Mais en fait, tu parles plus des autres fonctions et du type de retour que de la fonction main. A mon avis : plus insister sur les differentes synthaxes deja vue pour appeler des fonctions, puis aborder main pour introduire comment definir une fonction

Par exemple :

  1. principe general d’une fonction : prendre des infos en entree (optionel) -> faire un traitement -> retourner une info (optionel)

  2. exemple de fontions deja vu :

// fonction libre vs fonction membre
std::begin(str)
str.begin();
// fonction qui ne prend pas d'info et retourne rien
std.clear();

// fonction qui retourne quelque chose
auto const it = str.begin();

fonction qui prend des infos en entree
str.resize(100);
  1. un exemple deja vu de creation de fonction
int main (int argc, char* argv[]) { 
    // body 
}

avec 'int' = information (parametre) retourné, 'int argc, char* argv[]' = informations en entree, 'body' = les instructions qui constitueront le traitement que la fonction realise.

  1. presenter le plan du chapitre :
  • declaration de fonction
  • declaration de parametres en entree ou rien (+ comment appeler la fonction en passant des arguments + comment utiliser les parametres dans le corps)
  • retourner quelque chose ou rien (+ comment retourner quelque chose + comment recuperer l’info dans la code appellant + on n’est pas obligé d’utiliser la valeur retournée)
  • note sur les contrats et erreur : dans un chapitre suivant

Déployons la toute puissance des conteneurs

  • pas sur de la place de ce chapitre. C’est un chapitre qui ne concerne pas la creation de fonctions, au milieu de 3 chapitres sur la creation de fonctions (8, 9 et 11). Et le 10 n’a pas besoin d’etre lu specifiquement etre 9 et 11.

Peut etre changer l’ordre :

  • 1 a 7 + 10 : programmation structurée
  • 8, 9 et 11 : creer ses propres fonctions

Surtout que j’imagine qu’il y a encore des choses a dire sur les fonctions.

Composants d’une fonction

"ceci étant réservé pour le compilateur" : plutot dire qu’il y a des regles speciales si ca commence par underscore et qu’il est donc plus simple de ne pas faire.

"Par contre, tout le code situé après un return n’est jamais exécuté" : peut etre preciser que le compilateur peut signaler ce probleme ? "warning: code will never be executed [-Wunreachable-code]" (clang)

"Soit la fonction retourne une valeur" : peut etre preciser que c’est optionel de recuperer cette valeur retourner ?

int foo() { return 123; }

int const i = foo(); // ok
foo(); // ok aussi

En soi, ce n’est pas tres important, sauf dans un cas particulier : lorsque la fonction informe si la fonction a echouer ou non. Dans ce cas, c’est autorise par le compilateur de ne pas recuperer la valeur, mais en pratique, c’est une mauvaise chose. TOUJOURS recuperer ce type de valeur et les verifier.

"Mais la question est : comment ?" : je me pose beaucoup de question sur ce type de questions rethoriques. Utile ? Lourd ? Facilite la lecture. Je ne sais pas trop.

"int repetitions { 3 };" : manque de const.

"Maintenant, on veut retourner un booléen, pour indiquer si tout c’est bien passé." : peut etre preciser que la gestion des erreurs dans les fonctions est une question plus complexe qu’il n’y parait et qu’il y a un chapitre dedie sur le sujet.

"On peut très bien lui donner en argument" : un petit mot par rapport aux types des arguments ? Si les types ne correspondent pas exactement, cela peut fonctionner quand meme. (coercision, promotion, cast). Mais trop complexe pour le moment, donc utiliser exactement le meme type pour les arguements et parametres. Cas particulier de bool ? (cast to int) Peut etre interdire bool en parametre…

"int pgcd(int a, int b)" : j’aime bien "lhs" et "rhs". Ca fait plus "pro" quand on utilise ca :D

Prototypes de fonctions

"le compilateur ne va pas apprécier" : peut preciser que le compilateur interprete le code ligne par ligne ? Quand il lit une ligne, il connait les lignes au dessus, mais pas encore celle en dessous.

Paramètres obligatoires et optionnels

"des arguments dits optionnels" puis "un paramètre optionnel" puis "les paramètres facultatifs" : parametere ou argument optionel ? Dans la norme, "default argument". Peut utiliser ce terme plutot que "optionel" ?

Quelles sont vos références ?

Polymorphisme ad-hoc

Il existe 2 types de polymorphisme ad-hoc : la coercition (le type de l’argument est converti pour correspondre au type du parametre) et la surcahrge. Ici, tu parles que de la surcharge. cf https://cpp.developpez.com/faq/cpp/?page=Programmation-objet-en-Cplusplus#Qu-est-ce-que-le-polymorphisme

Pour poser des questions ou simplement discuter informatique, vous pouvez rejoindre le discord NaN.

+2 -0

Pas de soucis, c’est normal. Et j’apprécie toujours autant tes retours. :)

Je suis sur téléphone et je ne peux pas citer, parce que trop galère, mais je prends en compte la plupart de tes remarques. Je voudrais surtout réagir à propos de l’ordre des chapitres.

Au début, je me disais que je n’allais pas le faire. Mais en réfléchissant, je me dit que c’est une bonne idée. Cela implique un effort de réécriture de certaines portions, mais ça peut valoir le coup. Je pourrais, après le chapitre « Au tableau », parler des itérateurs et algorithmes, puis ensuite de la lecture / écriture des fichiers, pourquoi pas présenter std::map, les itérateurs sur les flux, etc. Il y aurait ensuite une section sur les fonctions, la gestion des erreurs, le découpage en fichiers, les temmplates, lambdas, etc.

Qu’en pense-tu ?

Les fichiers presentent peut etre des problematiques trop specifiques pour etre mis avec les conteneurs. Je le mettrais a part.

Pour le reste, ca me semble pas mal comme organisation.

Attention de ne pas perdre de vue quel public tu vise. N’hesites pas a supprimer.

Et peut etre faire la reorganisation pres publication de la premiere version ?

Pour poser des questions ou simplement discuter informatique, vous pouvez rejoindre le discord NaN.

+0 -0

Je ne pensais a aucun chapitre, ni meme a des parties en particulier. C’est juste que lorsqu’on relis, on a tendance a se dire "tiens, je peux parler de ca aussi, parler de ca, etc.". Et on perds l’objectif, le public visé, la coherence…

"pres" = "apres". Eviter de trop en faire maintenant, pour publier rapidement. Et continuer ensuite. (Dans mes relectures, je ne trie pas du tout mes remarques. Certaines sont importantes, mais la plupart peuvent etre reportés sans probleme.

Pour poser des questions ou simplement discuter informatique, vous pouvez rejoindre le discord NaN.

+0 -0

Salut à tous,

Dans la suite des messages échangés avec @gbdivers, je fais un changement dans le plan de ce cours. Les plus grosses différences se trouvent dans le chapitre sur les algorithmes, puisque j’en ai supprimé une partie. J’ai cependant décidé d’introduire les prédicats standards, en montrant déjà des cas concrets. Hormi le chapitre sur la manipulation des fichiers, dont je n’ai pas encore décidé de sa pertinence à cet endroit, la première partie est prête.

Dans la deuxième partie, j’aborde le découpage. Cela implique les fonctions avec la gestion des erreurs, mais également les namespaces, les fichiers, les lambdas. Il y a beaucoup à dire à propos des fonctions, notamment comment les écrire, les transmettre, la généricité, le trailing return type. Je vais voir comment répartir tout ça.

Merci d’avance pour vos commentaires.

Édité par informaticienzero

Vous devez être connecté pour pouvoir poster un message.
Connexion

Pas encore inscrit ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte