#include <string> semble ne pas avoir d'influence sur le resultat

Le problème exposé dans ce sujet a été résolu.

Bonjour à tous et toutes,

je débute en C++ et j’ai un problème avec le type string. Je lis énormément que pour utiliser le type string, il faut avoir fait au préalable un #include <string>. Le bout de code suivant utilise le type string et fonctione sans cette inclusion. Je ne recois aucun message d’avertissement ni d’erreur.

#include <iostream>

int main() {
    std::string name {"Name"};
    std::cout<<name<<std::endl;
    
    std::string var1 = "je";
    std::string var2 = " suis";
    std::string var3 = var1 + var2;
    std::cout << var3 << std::endl;

    return 0;
}

Je sais qu’il est préférable de faire cette Include mais, j’aimerais svp avoir des explications sur ce comportement.

Merci d’avance

+0 -0

J’imagine que le fichier d’en-tête standard iostream fait lui-même #include <string> parce qu’il en a besoin, et par conséquent ton fichier en bénéficie même si tu ne l’as pas inclus directement.

Ça reste préférable d’inclure string toi-même, pour une raison simple : si pour une raison ou pour une autre un jour ton fichier .c n’inclut plus iostream, tu obtiendras d’un coup des erreurs sur les types et fonctions de l’en-tête string. Bien sûr sur un petit fichier comme ça ça ne prête pas à conséquence, mais dans un gros projet mieux vaut éviter de se reposer sur des includes indirects sous peine d’avoir parfois des comportements surprenants.

Salut,

Pour compléter la réponse d'@otini, j’ajouterais que cela s’appelle une dépendance transitive1. On retrouve ce concept un peu partout, notamment dans les gestionnaires de dépendances en programmation ou dans les gestionnaires de paquets sur les distributions Linux.

Concrètement, cela veut dire que tu utilises une dépendance BB d’une de tes dépendances AA, et c’est considéré comme une mauvaise pratique, car cela crée une faiblesse dans ton propre code : si ta dépendance AA vient à supprimer la dépendance BB pour une raison ou pour une autre, ou si elle la met à jour vers une version incompatible avec ton code, cela casse ce dernier.

Ainsi, même si ça marche de faire sans, la bonne pratique est d’ajouter tout ce dont tu as besoin dans tes dépendances, même si elle est techniquement déjà présente grâce à une autre de tes dépendances.


  1. Tentative désespérée de traduire le terme anglais transitive dependency, qui ne semble pas avoir de traduction en français. Doh !
+2 -0

@Deuchnord: Je ne vois pas de problème avec dépendance transitive qui me semble être la traduction littérale et directe courante et utilisée de transitive dependency.

+3 -0

@Deuchnord: Je ne vois pas de problème avec dépendance transitive qui me semble être la traduction littérale et directe courante et utilisée de transitive dependency.

ache

Je n’ai jamais entendu le terme en français à vrai dire, seulement lu dans des textes en anglais, d’où mon annotation sur le mot :)

+1 -0

salut salut :),

déjà merci pour vos retours. @otini je suis allé jeter un coup d’œil dans le fichier d’en-tête. Je n’ai pas trouvé d’include pour les string même en parcourant les fichiers qui sont inclus dans le iostream.

C’est peut-être due à l’espace de nom std, mais le thème des espaces de nom m’est vraiment compliqué à saisir. Si quelqu’un connait un article facile à comprendre sur ce sujet, je suis preneur.

Merci encore

Kev_k

Salut,

Je n’ai pas trouvé d’include pour les string même en parcourant les fichiers qui sont inclus dans le iostream.

C’est relativement bien caché. Chez moi (GCC 12.2), la chaine de dépendance est la suivante :

iostream
ostream
ios
bits/ios_base
bits/locale_classes.h
string

Il y a peut être un chemin plus court que j’ai pas trouvé, mais c’est pas très important.

À noter que ce que tu observes est en fait relativement orthogonal à la notion de dépendance transitive (d’ailleurs, les différentes parties de la stdlib dépendent circulairement entre eux, d’où le besoin des forward declarations:-° ). Le vrai problème est que les header files ont des possibilités de scoping relativement limitées, ce qui fait que les include successifs polluent tous les dépendants. On se retrouve alors avec un système casse-gueule où :

  • certaines inter-dépendances sont standard, par exemple <ostream> est forcément inclus dans <iostream>, c’est pour ça par exemple qu’il est considéré OK d’utiliser std::endl (qui vient de <ostream>) si tu as inclus <iostream> ;
  • certaines inter-dépendances sont spécifiques à l’implémentation de la lib C++ utilisée (comme le fait que <string> est inclus dans <iostream> avec GCC 12.2), et il n’est pas robuste de s’appuyer sur ce second cas.

Évidemment, il n’y a aucun moyen de différencier les dépendances standards de celle qui ne le sont pas simplement en inspectant les fichiers (ou en essayant ce qui marche), donc t’es obligé de te cogner la doc pour vérifier et/ou d’utiliser des linters qui vérifient ce genre de choses (ce qui factorise la question d’aller voir la doc en la reléguant aux gens qui développent les linters).

Dans un langage sain avec des mécanismes pour gérer les portées des différents symboles, t’as pas ce problème évidemment.

Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

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