FAQ C++Consultez toutes les FAQ
Nombre d'auteurs : 34, nombre de questions : 368, dernière mise à jour : 14 novembre 2021 Ajouter une question
Cette FAQ a été réalisée à partir des questions fréquemment posées sur les forums de http://www.developpez.com et de l'expérience personnelle des auteurs.
Je tiens à souligner que cette FAQ ne garantit en aucun cas que les informations qu'elle propose sont correctes ; les auteurs font le maximum, mais l'erreur est humaine. Cette FAQ ne prétend pas non plus être complète. Si vous trouvez une erreur ou si vous souhaitez devenir rédacteur, lisez ceci.
Sur ce, nous vous souhaitons une bonne lecture.
- Qu'est-ce que Boost ?
- Comment installer boost ?
- Où trouver de la documentation sur Boost ?
- Comment utiliser les pointeurs intelligents de Boost ?
- Quelles sont les possibilités de casting sur un shared_ptr ?
- Quels sont les possibilités de conversion (casting) proposées par Boost ?
- Comment découper une chaîne avec boost::tokenizer ?
- [Exemple] Comment détruire les pointeurs d'un conteneur en utilisant Boost ?
- Qu'est-ce que Boost.Variant ?
- Comment récupérer la valeur contenue dans un boost::variant ?
- Qu'est-ce que boost::any et boost::variant et quand les utiliser ?
- Peut-on utiliser la bibliothèque boost avec Visual C++ ?
Boost est un ensemble de bibliothèques C++ gratuites et portables dont certaines seront intégrées au prochain standard C++ (voir Qu'est-ce que le Library Technical Report (tr1 / tr2) ?). On y retrouve donc naturellement les concepts de la bibliothèque standard actuelle, et en particulier ceux de la STL avec laquelle elle se mélange parfaitement. Boost est très riche : elle fournit notamment des bibliothèques pour :
- les threads (Boost.Thread) ;
- les matrices (uBLAS) et les tableaux à dimensions multiples (Boost.MultiArray) ;
- les expressions régulières (Boost.Regex) ;
- la méta-programmation (Boost.Mpl) ;
- l'utilisation de foncteurs (Boost.lambda, Boost.bind) ;
- la date et l'heure (Boost.Date_Time) ;
- les fichiers et les répertoires (Boost Filesystem) ;
- gérer la mémoire avec des pointeurs intelligents (Smart Pointers) ;
- faire de la sérialisation en binaire / texte / XML, en particulier sur les conteneurs standards (Boost Serialization) ;
- manipuler des graphes mathématiques (Boost Graph) ;
- manipuler les chaînes de caractères (Boost String Algorithms) ;
- la création de parsers (Boost.spirit, et plus récemment Spirit X3) ;
- et bien d'autres…
La liste complète des bibliothèques classées par catégories est disponible ici : http://www.boost.org/libs/.
La plupart de ces bibliothèques tentent d'exploiter au maximum les possibilités du langage C++.
En fait, Boost se veut un laboratoire d'essais destiné à expérimenter de nouvelles bibliothèques pour le C++. Il s'agit donc aussi d'une communauté d'experts (dont plusieurs sont membres du comité ISO de normalisation du C++) qui mettent un point d'honneur à ce qu'un maximum de compilateurs et de systèmes soient supportés. Ils débattent aussi de l'acceptation de nouvelles bibliothèques et l'évolution de celles déjà existantes, préfigurant ainsi ce que à quoi ressemblera certainement la prochaine bibliothèque standard du C++ (voir Qu'est-ce que C++0x ?).
C'est donc là que réside le grand intérêt de Boost. Outre son excellence technique et sa licence très permissive (compatible avec la GPL) qui permet de l'utiliser gratuitement dans des projets commerciaux, Boost est aussi un choix très viable sur le long terme. En effet, on peut légitimement espérer qu'un nombre important de ses bibliothèques soient un jour standardisées, ce qui en fait un outil dans lequel on peut investir du temps (et donc de l'argent) sans craindre de tout perdre au bout de quelques années faute de support ou d'évolution.
Une bonne partie des bibliothèques qui composent Boost peuvent être utilisées directement, sans nécessiter aucune compilation. Si, dans un premier temps, votre utilisation de Boost se limite à ce genre de bibliothèque, installer Boost consiste simplement à rendre ses fichiers d'en-tête accessibles à votre compilateur (INCLUDE PATH). Référez vous à la documentation de ce dernier pour savoir comment procéder. Quant aux autres bibliothèques bâties sur des appels système telles que boost::filesystem, boost::date_time, elles nécessitent auparavant d'être compilées. Pour cela, Boost utilise son propre système de génération de type make : Boost.Jam, ou plus simplement bjam. Il faut d'abord compiler ce dernier (ou récupérer une version compilée), avant de l'utiliser pour compiler la bibliothèque. Pour plus d'information sur la compilation de Boost, référez-vous à la documentation disponible en ligne ou encore dans le répertoire /tools/build/. Pensez aussi à effectuer une recherche sur nos forums.
Enfin, les utilisateurs de Visual C++ peuvent utiliser la version prête à l'emploi gracieusement mise à leur disposition par : Boost Consulting. Vous pouvez lire à ce sujet le tutorial Installer et utiliser Boost sous Windows avec Visual C++ 2005.
La principale source d'information sur Boost est la documentation officielle de chaque bibliothèque disponible sur le site de Boost. En dehors de cela, il existe malheureusement assez peu de références sur le sujet.
Citons néanmoins les tutoriels de Miles, en français : Un aperçu des possibilités des bibliothèques de Boost.
Ainsi que quelques livres (en anglais) :
- Beyond the C++ Standard Library An Introduction to Boost dont 1 chapitre est disponible ici : Library 9 - Bind.
- C++ Template Metaprogramming : Concepts, Tools, And Techniques From Boost And Beyond.
- The Boost Graph Library: User Guide and Reference Manual.
Et bien sûr la présente FAQ qui comporte une section consacrée à Boost.
Boost met à notre dispositions plusieurs types de pointeurs intelligents (voir Boost Smart Pointers). Les plus couramment utilisés sont boost::shared_ptr et boost::shared_array (pour les tableaux) qui sont des pointeurs intelligents fonctionnant par comptage de référence :
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | #include <iostream> #include <string> #include <boost/shared_ptr.hpp> class Test { public: Test( const char * Name ): name( Name ) { } ~Test() { std::cout << "Destruction de " << this->name << '\n'; } void printName() { std::cout << this->name << '\n'; } private: std::string name; }; // déclaration du type pointeur intelligent sur Test typedef boost::shared_ptr<Test> TestPtr; int main() { TestPtr ptr; // pointeur initialisé à NULL { // pointeur temporaire détruit à la fin du bloc TestPtr ptr_tmp( new Test( "objet1" ) ); // initialiser ptr avec ptr_tmp ptr = ptr_tmp; } // ici, ptr_tmp est détruit, mais ptr reste valide ptr->printName(); // OK, affiche "objet1" // réinitialiser le pointeur avec un nouvel objet // objet1 est détruit, objet2 est créé ptr.reset( new Test( "objet2" ) ); ptr->printName(); // OK, affiche "objet2" // copie du pointeur sur objet2 TestPtr ptr2 = ptr; // mise à NULL de ptr ptr.reset(); // rien ne se passe // mise à NULL de ptr2 ptr2.reset(); // objet2 est détruit // utilisation du pointeur NULL : erreur en mode Debug ptr->printName(); // Assertion failed: px != 0 } |
Destruction de objet1
objet2
Destruction de objet2
Assertion failed: px != 0
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 | struct Delete { void operator ()(Test*& Ptr) const { cout << "Destruction"; delete Ptr; } }; int main() { shared_ptr<Test> Ptr(new Test(), Delete()); } |
Elles sont très nombreuses, et équivalentes à celles sur les pointeurs bruts dans leur grande majorité.
Prenons l'exemple de base suivant :
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | #include <boost/shared_ptr.hpp> class Base { public: virtual ~Base() {}; }; class Derived : public Base {}; typedef boost::shared_ptr<Base> BasePtr; typedef boost::shared_ptr<const Base> BaseConstPtr; typedef boost::shared_ptr<Derived> DerivedPtr; typedef boost::shared_ptr<const Derived> DerivedConstPtr; |
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | void implicit_upcasting() { // casting implicite sur des pointeurs bruts { Derived *d = new Derived; Base *b1 = d; const Base *b2 = d; delete d; } // équivalent avec des pointeurs intelligents { DerivedPtr d( new Derived ); BasePtr b1 = d; BaseConstPtr b2 = d; } } |
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | void down_casting() { // downcasting sur des pointeurs bruts { Base *b = new Derived; Derived *d1 = static_cast<Derived*>( b ); Derived *d2 = dynamic_cast<Derived*>( b ); assert( d2 != 0 ); delete b; } // équivalent avec des pointeurs intelligents { BasePtr b( new Derived ); DerivedPtr d1 = boost::static_pointer_cast<Derived>( b ); DerivedPtr d2 = boost::dynamic_pointer_cast<Derived>( b ); assert( d2 != 0 ); } } void const_casting() { // constcasting sur des pointeurs bruts { const Base *b_const = new Base; Base *b2 = const_cast<Base*>( b_const ); delete b_const; } // équivalent avec des pointeurs intelligents { BaseConstPtr b_const( new Base ); BasePtr b2 = boost::const_pointer_cast<Base>( b_const ); } } |
- const_pointer_cast ;
- static_pointer_cast ;
- dynamic_pointer_cast.
ont été ratifiées par le comité de normalisation ISO et incluses dans le Technical Report 1 (tr1). Ce n'est pas le cas de quatre autres fonctions, qui ont été déclarées obsolètes :
- shared_static_cast ;
- shared_dynamic_cast ;
- shared_polymorphic_cast ;
- shared_polymorphic_downcast.
Les deux premières sont respectivement équivalentes à static_pointer_cast et dynamic_pointer_cast, et leur usage est donc fortement découragé. Les deux dernières en revanche n'auront pas d'équivalent dans le prochain standard. Elles correspondent en fait à boost::polymorphic_cast et boost::polymorphic_downcast appliqués aux shared_ptr (voir Comment utiliser les pointeurs intelligents de Boost ?).
L'exemple suivant illustre une possible utilisation de ces deux fonctions :
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | void deprecated_casting() { // downcasting sur des pointeurs bruts { Base *b = new Derived; try { // downcasting levant std::bad_cast en cas d'échec Derived &d1 = dynamic_cast<Derived&>( *b ); } catch ( const std::bad_cast & ) { } // downcasting provoquant une erreur uniquement en debug #ifdef _DEBUG Derived *d2 = dynamic_cast<Derived*>( b ); assert( d2 ); #else Derived *d2 = static_cast<Derived*>( b ); #endif delete b; } // équivalent avec des pointeurs intelligents { BasePtr b( new Derived ); try { // downcasting levant std::bad_cast en cas d'échec DerivedPtr d1 = boost::shared_polymorphic_cast<Derived>( b ); } catch ( const std::bad_cast & ) { } // downcasting provoquant une erreur uniquement en debug DerivedPtr d2 = boost::shared_polymorphic_downcast<Derived>( b ); } } |
Pour terminer, rappelons qu'il est possible de construire un shared_ptr à partir d'un std::auto_ptr (qui est alors invalidé par le shared_ptr construit), ce qui peut s'apparenter en quelque sorte à un cast d'auto_ptr en shared_ptr.
Code c++ : | Sélectionner tout |
1 2 3 | std::auto_ptr<int> p1( new int ); boost::shared_ptr<int> p2( p1 ); // ici, p1 est invalide |
boost::conversion introduit quatre types de cast sous forme de fonctions templates libres :
- polymorphic_cast ;
- polymorphic_downcast ;
- lexical_cast ;
- numeric_cast.
polymorphic_cast s'utilise comme dynamic_cast, mais contrairement à ce dernier qui possède un comportement différent en fonction du type casté (en cas d'erreur), polymorphic_cast lève systématiquement une exception std::bad_cast en cas d'échec. Son comportement est donc le même que celui de dynamic_cast en cas de conversion de références, et c'est précisément pourquoi polymorphic_cast n'est pas prévu pour être utilisé avec ces dernière.
Notez que polymorphic_cast peut être utilisé pour effectuer du cross-casting. Si vous utilisez dynamic_cast pour effectuer du downcasting (ou crosscasting) qui ne devrait jamais échouer, pensez à utiliser polymorphic_cast qui vous économisera de tester le résultat du cast et permet aussi de mieux signaler dans le code l'intention d'effectuer un cast qui ne devrait pas échouer.
Si l'utilisation de dynamic_cast vous procure des problème de performance dans votre programme, (ce qui devrait traduire un problème de conception, voir Pourquoi l'utilisation du downcasting est-il souvent une pratique à éviter ?) la solution habituelle est d'utiliser static_cast en remplacement.
Ce dernier est bien plus performant, mais aussi beaucoup plus risqué dans la mesure où le compilateur vous fait pleinement confiance, et est incapable de vous signaler une erreur (ce que dynamic_cast ou polymorphic_cast savent faire).
polymorphic_downcast est une sorte de compromis entre ces deux choix.
Compilé en version de développement (DEBUG), polymorphic_downcast se comporte un peu comme polymorphic_cast, sauf qu'en cas d'échec une assertion failure est déclenchée au lieu d'une exception. Dans le code de production (RELEASE), son appel est remplacé par un simple appel à static_cast, permettant ainsi d'obtenir un programme final performant sans trop pénaliser la fiabilité.
polymorphic_downcast est malgré tout à utiliser avec retenu, en tant qu'optimisation après qu'un problème de performances ait été identifié, et si ce dernier ne peut pas être résolu en reconsidérant le design de l'application.
À noter aussi, que contrairement à dynamic_cast et donc polymorphic_cast, polymorphic_downcast ne peut pas être utilisé pour du crosscasting.
Le programme suivant illustre comment utiliser boost::tokenizer pour découper une chaîne de caractères selon des séparateurs par défaut, ou selon une liste de séparateurs bien précis :
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | #include <iostream> #include <boost/tokenizer.hpp> // découpe la chaine avec les séparateurs par défaut void split( const std::string & Msg ) { // utiliser le tokenizer par défaut boost::tokenizer<> tok( Msg ); // itérer la séquence de tokens for ( boost::tokenizer<>::const_iterator i = tok.begin(); i != tok.end(); ++i ) { // afficher chaque token extrait std::cout << *i << '\n'; } } // découpe la chaine selon les séparateurs donnés void split( const std::string & Msg, const std::string & Separators ) { // typedef pour alléger l'écriture typedef boost::tokenizer<boost::char_separator<char> > my_tok; // séparateur personnalisé boost::char_separator<char> sep( Separators.c_str() ); // construire le tokenizer personnalisé my_tok tok( Msg, sep ); // itérer la séquence de tokens for ( my_tok::const_iterator i = tok.begin(); i != tok.end(); ++i ) { // afficher chaque token extrait std::cout << *i << '\n'; } } int main() { std::cout << "-- exemple 1 --\n"; split( "mot1;mot2; ;mot3;;mot4;mot5;" ); std::cout << "-- exemple 2 --\n"; split( "mot-compose1;mot,compose2;[mot][compose3];mot compose4;<mot><compose><5>", ";" ); } |
Code : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 | -- exemple 1 -- mot1 mot2 mot3 mot4 mot5 -- exemple 2 -- mot-compose1 mot,compose2 [mot][compose3] mot compose4 <mot><compose><5> |
La question Comment supprimer correctement des éléments d'un conteneur ? illustre comment supprimer les pointeurs d'un conteneur au moyen de std::for_each et d'un foncteur fait sur mesure. Voici deux autres possibilités équivalentes utilisant Boost, afin de vous faire une idée de ses possibilités.
La première combine std::for_each avec un foncteur de Boost : boost::checked_deleter, et la seconde utilise Boost.Foreach :
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | #include <list> #include <algorithm> #include <boost/checked_delete.hpp> int main() { // Création d'une liste de pointeurs std::list<int*> l; l.push_back(new int(5)); l.push_back(new int(0)); l.push_back(new int(1)); l.push_back(new int(6)); // Destruction de la liste : attention il faut bien libérer les pointeurs avant la liste ! std::for_each(l.begin(), l.end(), boost::checked_deleter<int>()); return 0; } |
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | #include <list> #include <algorithm> #include <boost/foreach.hpp> int main() { // Création d'une liste de pointeurs std::list<int*> l; l.push_back(new int(5)); l.push_back(new int(0)); l.push_back(new int(1)); l.push_back(new int(6)); // Destruction de la liste : attention il faut bien libérer les pointeurs avant la liste ! BOOST_FOREACH( int *pi, l ) { delete pi; } return 0; } |
En programmation, dans certains langages, on a ce que l'on appelle les types somme. Il s'agit en fait de décomposer un type T en plusieurs sous-types T1, T2,… , TN. Une instance de T peut être obtenue par une valeur de type T1 ou T2 ou T3, mais pas deux types à la fois. Cela correspond vaguement aux unions présentes en C et en C++. Par exemple, si vous réalisez un interpréteur d'expressions mathématiques du type '1+2-4', alors vous construirez généralement un arbre d'expressions, une expression étant soit un nombre, soit une opération (+, -…) mettant en relation deux nombres, qui elle-même résultera en un nombre. Toutefois une expression ne peut pas être à la fois un nombre et une opération mettant en relation deux expressions.
Dans les deux cas, nous décomposons notre type 'expression' qui peut-être vu comme une union disjointe de deux types. C'est ce à quoi sert le type union en C et C++, toutefois il ne permet pas de gérer des classes dès qu'elles ont un constructeur par exemple.
En C++, un telle décomposition est rendue possible (bien que moins puissante et absolument pas intégrée au langage lui-même) grâce à Boost.Variant. En effet, nous pouvons définir un type C++ qui représente également l'union disjointe de deux ensembles.
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 | class A { }; class B { }; class C {}; class D {}; boost::variant<A,B,C,D,std::string,int> v; v = A(); // v contient une valeur de type A v = B(); // v contient une valeur de type B v = C(); // v contient une valeur de type C v = D(); // v contient une valeur de type D v = "Salut"; // v contient une valeur de type std::string v = 42; // v contient une valeur de type int |
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | struct Op { enum op_type { ADD, SUB }; double e1, e2; op_type op; }; double compute_op(const Op& o) { switch(o.op) { case Op::ADD: return o.e1 + o.e2; break; case Op::SUB: return o.e1 - o.e2; break; } } typedef boost::variant<double, Op> expression; double compute_expression(const expression& e) { if( double* d = boost::get<double>(&e) ) { return d; } Op* o = boost::get<Op>(&e); return compute_op(*o); } |
La fonction template boost::get(), définie dans <boost/variant/get.hpp>, est un premier moyen de récupérer la valeur d'un boost::variant. Il en existe 4 versions :
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 | template<typename U, typename T1, typename T2, ..., typename TN> U * get(variant<T1, T2, ..., TN> * operand); // (1) template<typename U, typename T1, typename T2, ..., typename TN> const U * get(const variant<T1, T2, ..., TN> * operand); // (2) template<typename U, typename T1, typename T2, ..., typename TN> U & get(variant<T1, T2, ..., TN> & operand); // (3) template<typename U, typename T1, typename T2, ..., typename TN> const U & get(const variant<T1, T2, ..., TN> & operand); // (4) |
- La première version travaillera sur un pointeur vers boost::variant pour vous retourner un pointeur vers la valeur voulue.
- La seconde version travaillera sur un pointeur vers un boost::variant constant pour vous retourner un pointeur vers la valeur constante voulue.
- La troisième version travaillera sur une référence vers un boost::variant pour vous retourner une référence sur la valeur voulue.
- La quatrième version travaillera sur une référence sur un boost::variant constant pour vous retourner une référence sur la valeur constante voulue.
Dans le cas de (1) et (2), si le get échoue (si votre variant contient un int et que vous appelez get<string>(v) par exemple), alors la fonction vous retourne un pointeur nul.
Dans le cas de (3) et (4), si le get échoue, la fonction lance une exception bad_get (qui dérive de std::exception et définit donc la fonction what() décrivant ce qu'il s'est passé).
De manière générale, la fonction get échouera (retournera un pointeur nul pour (1) et (2), lancera une exception bad_get pour (3) et (4)) si la valeur courante contenue dans votre boost::variant n'est pas du type demandé explicitement avec get().
Pour terminer, un petit exemple d'utilisation :
Code c++ : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | #include <iostream> #include <boost/variant.hpp> int main() { boost::variant<int, std::string> v; v = 42; int* i = boost::get<int>(&v); // l'entier pointé par i vaut 42. assert(*i == 42); *i = 84; // cela a également modifié la valeur contenue dans v std::string* s = boost::get<std::string>(&v); assert(s == NULL); // s vaut effectivement le pointeur nul int& i2 = boost::get<int>(v); assert(i2 == 84); try { std::string& s = boost::get<std::string>(v); } catch (std::exception& e) { std::cout << "Exception ! " << e.what() << std::endl; } return 0; } |
Exception ! boost::bad_get : failed value get using boost::get.
Il y deux moyens pour simuler un typage faible en C++. On parle bien de simulation, le langage reste typé statiquement.
Le premier d'entre eux est Boost variant :
Code c++ : | Sélectionner tout |
1 2 3 4 | boost::variant< int, string > x; // déclare une variable de type boost::variant en précisant les types autorisés. x = 42; //x contient un entier x = "hello, world"; // x contient une chaine de caractères x = new Widget(); //erreur, x ne peut pas contenir un Widget. |
Vous pouvez même simuler un comportement polymorphe ad-hoc (surcharge de fonctions) avec boost::apply_visitor qui sera en plus vérifié à la compilation.
L'autre moyen est boost::any :
Code c++ : | Sélectionner tout |
1 2 3 4 | boost::any x; x = 42; // x contient un entier x = "hello, world"; //x contient une chaine de caractères x = new Widget(); // x contient un widget, pas d'erreur |
De façon intéressante, ceci montre comme le C++ suit de façon ferme et efficace un schéma de typage statique quand c'est possible et dynamique quand c'est nécessaire.
Quand avez vous besoin de quoi ?
Utilisez boost::variant quand vous voulez :
- un objet capable de stocker les valeurs d'un nombre fini de types ;
- une vérification à la compilation du type visité ;
- une allocation efficace, qui se trouve sur la pile ;
- et vous pouvez vivre avec d'horribles messages d'erreur quand le type attribué n'est pas le bon.
Utilisez boost::any quand vous voulez :
- la flexibilité offerte par un objet capable de stocker virtuellement n'importe quel type ;
- la flexibilité offerte par any_cast ;
- la garantie qu'il n'y aura pas d'exceptions lancées durant un swap.
Oui, mais de manière confortable à partir de Visual C++ 7.1 seulement (Visual C++ .Net 2003).
Microsoft a consacré un article à ce sujet qui fait aussi office de bonne introduction à cette bibliothèque : Boost for Visual C++ Developers.
L'article Installer et utiliser Boost/Boost.TR1 avec Visual C++ vous donnera les bases pour réaliser cette installation
Proposer une nouvelle réponse sur la FAQ
Ce n'est pas l'endroit pour poser des questions, allez plutôt sur le forum de la rubrique pour çaLes sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2024 Developpez Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.