a) De plus, par rapport au transtypage, j'ai appris avec le tutoriel que void* est un fourre-tout pour tous les types.
b) Est-ce que , tout comme tu l'as fait pour passer d'un env* vers un void*, aurait-on le droit spécialiser d'un void* vers un int (ou bien ça devrait forcément être un int*) ?
a) Bin, si un cours dit ça, il faut juste le jeter.
void*, c'est pas un fourre tout. Une variable de type void* sert à contenir une adresse, mais l'adresse d'une chose dont le compilateur ignore le type. C'est donc un pointeur, mais on ne peut pas le déréférencer (lui appliquer * ou ->).
Le type void* a été introduit en C pour essayer d'arranger le merdier originel, où on utilisait char* comme pointeur générique, en confusionnant allègrement char avec octet, et pointeur avec entier au passage.
Il est assez grand pour contenir tous les types de pointeurs dispos sur l'architecture. Autrefois (70's), les pointeurs étaient de la même taille, qui était celle d'un entier. Maintenant on ne suppose plus cela, surtout que ce n'est pas vrai du tout. Donc on ne peut pas mettre un entier dans un pointeur ou le contraire (réponse à b), au moins sans danger pour la portabilité.
Dans l'embarqué, on peut avoir des pointeurs de taille différente selon qu'il s'agit de pointer en mémoire (qui est petite), ou en mémoire flash. Exemple sur arduino at mega, RAM de 8k (il suffit de 13 bits) et flash 256K (18 bits). Dans un cas, il faudra 2 octets, dans l'autre 3.
En C, les pointeurs sur void sont compatibles (pas besoin de cast) avec les pointeurs sur autre chose. Exemple
int i = 1234;
int * p_int;
void * p_void;
p_int = & i;
p_void = p_int; // pas de problème
p_int = p_void; // pareil
printf("%d\n", *p_int);
On reconnait facilement les cours de C qui ont été écrits à l'époque des dinosaures quand on y voit :
struct Truc *ptr = (struct Truc *) malloc(sizeof(struct Truc)); -- le cast est inutile
En C++ c'est différent. static_cast is the way to go.
ton message n'est passé correctement. Il y a une partie du mien qui est recopiée.
- Edité par YES, man il y a 9 minutes
J'ai la facheuse manie de commencer un message et de le publier pour le sauver bien avant qu'il soit fini. Et de trouver des choses à y rajouter. Il apparait donc incomplet. Désolé.
je suis arrivé sur l'utilisation de generate() avec algorithm.
Dans cours de mathieu, il y a l'exemple suivant :
#include <algorithm>
#include <vector>
using namespace std;
//Définition de Remplir…
int main()
{
vector<int> tab(100,0); //Un tableau de 100 cases valant toutes 0
Remplir f(0);
generate(tab.begin(), tab.end(), f);
//On applique f à tout ce qui se trouve entre begin() et end()
return 0;
}
Je me suis alors demandé quel lien on peut faire avec l'exemple donné par gbdivers il y a quelques jours qui fut le suivant :
Ben oui... for_each applique une fonction sur chaque element, generate applique une fonction pour modifier chaque element. Forcement que c'est un peu lié, ces fonctions font des choses assez proches. Comme toutes les fonctions qui parcourent les tableaux. Je ne suis pas sur de comprendre a quel type de lien tu t'attends.
Merci, tu as vu la subtilité. D'accord, donc for_each applique une fonction sur chaque élément (sans le modifier) tandis que generate() a la capacité de modifier les éléments du conteneur.
EDIT : voici de morceau de code que je viens de tester :
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class Remplir{
public:
Remplir(int i):m_valeur(i)
{}
int operator()(){
++m_valeur;
return m_valeur;
}
private:
int m_valeur;//{-1};
};
int main()
{
Remplir foncteur(0);
vector<int> v(100,0);
generate(v.begin(),v.end(),foncteur);
generate(v.begin(),v.begin()+10,foncteur);
En principe, puisque mon foncteur fait office de mémoire, j'aurais pensé qu'après le premier generate(), la valeur enregistrée est 100.
Pourtant lorsque j'applique le deuxième generate() et que j'affiche, il semblerait que ça m'affiche de 1 à 100 Et pas de 101 à 110 pour les 10 premiers éléments.
D’après la doc, generate est déclarée de la sorte :
template< class ForwardIt, class Generator >
void generate( ForwardIt first, ForwardIt last, Generator g );
Si je ne dit pas de bêtises, dans ton cas Generator sera remplace (si quelqu'un peut me rappeler le terme correct pour le choix du type des templates, je suis preneur) par Remplir, effectuant donc une copie. Du coup le premier generate travaille sur une copie de ton foncteur (le 2em generate aussi) ce qui explique pourquoi tes valeurs se reset a 0.
Ce qui se passe, c'est que le générateur est transmis par valeur à std::generate.
Si bien que, après le premier appel, si tu vérifie la valeur de m_valeur dans foncteur, tu remarquera qu'elle est toujours égale à ... 0, et que, du coups, il est "normal" que le deuxième appel à générate utilise les valeurs 0, 1, 2 ... 10
(d'ailleurs, cela n'a pas vraiment de sens de mettre m_valeur en accessibilité privée ici, ni même d'utiliser le mot clé class : pour les foncteurs, on utilise généralement struct, et on met généralement tout en public ... mais bon...)
Tu pourrais obtenir le résultat que tu souhaite en modifiant un tout petit peu ton foncteur pour lui donner la forme de
struct Remplir{
public:
int operator()(){
static int value{0};
++value;
return value;
}
};
Et en corrigeant du coup la fonction main pour lui donner la forme de
Mais cela t'empêcherait de définir la valeur de départ à utiliser.
Note que tu pourrais aussi envisager de définir m_valeur comme étant une variable statique (de préférence publique, pour pouvoir la modifier à ta guise), sous une forme proche de
struct Remplir{
public:
static int value;
int operator()(){
++value;
return value;
}
};
int Remplir::value = 0;
et l'utiliser alors sous une forme proche de
int main()
{
vector<int> v(100,0);
generate(v.begin(),v.end(),Remplir{}); //value == 0
generate(v.begin(),v.begin()+10,Remplir{}); //value == 100
/* OU - OU - OU */
Remplir::value = 150;
/* utilise les valeurs allant de 150 à 165 pour les
* éléments à l'indice 15 à 30
*/
generate(v.begin()+15, v.begin()+30, Remplir{});
}
Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
Plus serieusement, si le foncteur est copie et qu'on veut reutiliser le meme compteur, c'est peut etre que le foncteur ne devrait pas etre proprietaire du compteur.
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class Remplir{
public:
Remplir(int& i):m_valeur(i)
{}
int operator()(){
++m_valeur;
return m_valeur;
}
private:
int& m_valeur;
};
int main()
{
int count { 0 };
Remplir foncteur(count);
vector<int> v(100,0);
generate(v.begin(),v.end(),foncteur);
generate(v.begin(),v.begin()+10,foncteur);
}
Et voila pourquoi on n'utilise plus trop de foncteur depuis qu'on a les lambda :
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
int count { 0 };
vector<int> v(100,0);
generate(v.begin(),v.end(),[&count](){ return (++count); });
generate(v.begin(),v.begin()+10,[&count](){ return (++count); });
}
Aussi... Mais, vu que notre ami voulait utiliser les foncteurs, qui étaient peu ou prou la seule solution durant le paléolithique, je lui ai expliquer comment on faisait à l'époque
Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
Merci gbdivers pour ton post détaillé avec l'exemple des lambdas et Koala01 ainsi que Elried. je devrais bientôt passer à l'utilisation massive des lambdas lorsque je termine le cours d 'OC pour passer sur lambdas massives.
J'apprends les foncteurs actuellement. C'est une bonne entrée en matière.
C'est la forme -- apparue en C++11 -- que nous pourrions appeler "initialisation standardisée".
Avant C++11, nous avions autant de moyen de créer une variable que de catégories de donnée:
on déclarait une variable de type primitif sous la forme de int i = 135;;
on créait une instance de classe sous une forme proche de Maclasse obj s'il n'était pas nécessaire de fournir des argument au constructeur;
on créait une instance de classe sous une forme proche deMaClasse obj(param1, param2);; si on souhaitait utiliser un constructeur paramétré;
On déclarait des tableaux sous la forme de std::vector tab = {1, 2, 3, 4, 5};.
(je ne suis pas sur de ne pas en oublier )
C++11 est arrivé avec la notion de "liste d'initialisateurs" (à ne pas confondre avec la liste d'initalisation du constructeur ) qui nous permet d'initialiser n'importe quel type de variable de manière strictement identique : en plaçant les données nécessaires à l'initialisation entre accolade "{" et "}".
Nous pouvons donc désormais déclarer nos variables sous une forme qui serait proche de
int i{135}; pour les types primitifs
MaClasse obj{}; si on dispose d'un constructeur ne prenant aucun paramètre pour MaClasse
MaClasse obj{param1, param2, param3}; si on veut utiliser un constructeur paramétré pour MaClasse et
std::vector<int> tab{1, 2, 3, 4, 5}; pour créer un tableau
La grosse difficulté (qu'il est impossible de contourner) réside, justement, dans l'usage de std::vector (entre autres???), car cette classe dispose d'un constructeur proche de
un tableau contenant dix élément (initialisés par défaut, et donc vallant 0) sous la forme de std::vector<int> tab(10);
un tableau contenant dix élément (initialisés à 20) sous la forme de std::vector<int tab(10,20);
(le dernier paramètre par défaut est tellement particulier que je n'en parlerai pas )
Mais, dans l'absolu, cette possibilité nous offre une manière "presque unique" d'initialiser n'importe quel type de variable dés la déclaration
Ce qui se conçoit bien s'énonce clairement. Et les mots pour le dire viennent aisément.Mon nouveau livre : Coder efficacement - Bonnes pratiques et erreurs à éviter (en C++)Avant de faire ce que tu ne pourras défaire, penses à tout ce que tu ne pourras plus faire une fois que tu l'auras fait
Et sinon, pourquoi ne pas apprendre avec un cours à jour ? Parce que les 3/4 de tes questions concernent la syntaxe et le fonctionnement basique de C++.
Je ne sais pas comment tu t'y prends, mais ça fait un bon paquet de mois que tu nous rabâches les oreilles avec le cours d'OC, tu as l'intention de le finir un jour ?
× Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
× Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
Discord NaN. Mon site.
Discord NaN. Mon site.
Discord NaN. Mon site.
Discord NaN. Mon site.