web-dev-qa-db-fra.com

Comment initialiser un std :: stringstream?

J'ai besoin de concaténer une chaîne avec des entiers. Pour ce faire, j'utilise stringstream de la manière suivante:

int numPeople = 10;
stringstream ss;
ss << "Number of people is " << numPeople;

Et ça a marché. Mais j'essayais de le faire de la manière suivante:

int numPeople = 10;
stringstream ss << "Number of people is " << numPeople;

Et j'obtenais l'erreur suivante: "initialiseur attendu avant le jeton '<<'"

Pourquoi ai-je eu cette erreur? Pourquoi ne puis-je pas attribuer la valeur stringstream en même temps que je la déclare?

20
Dragons_Lair5
stringstream ss << "Number of people is " << numPeople;

Pourquoi ne puis-je pas attribuer la valeur stringstream en même temps que je la déclare?

Cela revient à espérer que cela fonctionnera:

int x + 3 + 9;

Le problème

Lorsque vous définissez et donnez une valeur à un objet, C++ vous permet d'appeler le constructeur, ce qui nécessite une liste d'expressions séparées par des virgules. Pour plus de commodité, la notation Type variable = value; Est appelée pour appeler Type variable(value);, mais ne fonctionne que pour une seule valeur.

Pour int, vous pouvez facilement corriger le code:

int x = 3 + 9;

... et cela fonctionne parce que "3 + 9" peut être évalué indépendamment d'abord pour donner une valeur saine à stocker dans x. Le comportement du compilateur pour l'opérateur + Sur ints fait ce que nous voulons : il produit le int résultat, nous voulons ensuite stocker dans x. Mais si vous essayez cela pour stringstream...

stringstream ss = "Number of people is " << numPeople;  // BROKEN

... cela ne fonctionnera pas, car "Number of people is " << numPeople doit d'abord être évalué mais est illégal - vous obtiendrez une erreur comme "error C2296: '<<' : illegal, left operand has type 'const char [20]'" - il ne donnera pas de valeur utile pour le constructeur stringstream. Le problème est que le compilateur essaie toujours d'appliquer l'opérateur de décalage au niveau du bit, ce qui n'a de sens que pour les nombres, car les surcharges pour << Que nous voudrions appliquer nécessitent un argument de gauche de type ostream&. C++ nécessite que la valeur évaluée à droite de = Soit évaluée indépendamment de l'affectation qui est finalement effectuée avec la valeur résultante, et à ce stade, le type de la variable en cours de construction n'est pas pertinent pour la façon dont l'évaluation est tenté sur l'expression affectée.

Une solution

C'est un peu un problème de poulet et d'oeufs ici, car vous avez en quelque sorte besoin de combiner les valeurs de droite que vous voulez dans le stringstream pour appeler le constructeur de stringstream, mais pour cela, vous besoin ... d'un stringstream. Vous pouvez réellement retirer cela avec un stringstream temporaire:

static_cast<std::ostringstream&&>(std::ostringstream() << "Number of people is " << numPeople)

La conversion est malheureusement nécessaire car les surcharges operator<< Gèrent stringstreams via des références à leur classe de base ostream, renvoyant un ostream&, Vous devez donc effectuer une conversion en le type stringstream manuellement, vous pouvez donc invoquer le constructeur de déplacement std::stringstream ...

La construction complète à un revêtement est alors ...

std::stringstream ss(static_cast<std::ostringstream&&>(std::ostringstream() << "Number of people is " << numPeople));

... mais c'est trop hideux pour être envisagé.

Rendre la solution (sans doute) moins hideuse

Selon vos sensibilités, vous pouvez sentir qu'une macro aide ou est pire ...

#define OSS(VALUES) \
    static_cast<std::ostringstream&&>(std::ostringstream() << VALUES)

std::stringstream ss(OSS("Number of people is " << numPeople));

FWIW, vous pouvez également utiliser la macro pour créer des chaînes ...

std::string s(OSS("Number of people is " << numPeople).str()); 

Une (sans doute) meilleure pratique

Créez simplement le stringstream - en fournissant éventuellement un seul string au constructeur - puis utilisez operator<< Dans une deuxième instruction:

std::stringstream ss;
ss << "Number of people is " << numPeople;

C'est beaucoup plus facile à lire. Avec la construction de mouvements, après l'optimisation, il n'y a probablement aucune raison de performances pour préférer deux instructions.

Une alternative

C++ 11 introduit to_string() surcharges qui sont pratiques si vous avez une valeur intégrale ou deux à concaténer avec ou dans un string:

std::string s = "Number of people is " + std::to_string(numPeople);

Cela peut cependant être inefficace (vérifiez vos capacités d'optimisation de compilateur (s) si vous vous souciez): chaque std::to_string() est susceptible d'allouer dynamiquement un tampon pour une instance indépendante std::string, Alors les concaténations individuelles peuvent impliquent une copie supplémentaire du texte, et les tampons d'origine alloués dynamiquement devront peut-être être agrandis, alors la plupart de ces std::string temporaires prendront du temps à se désallouer pendant la destruction.

C'était pire en C++ 03

C++ 03 manquait de constructeurs de déplacement, il était donc nécessaire d'utiliser la fonction membre std::ostringstream::str() sur le temporaire pour obtenir une copie supplémentaire en profondeur du std::string Avec pour construire le stringsteam... nommé.

stringstream ss(static_cast<std::ostringstream&>(std::ostringstream() << "Number of people is " << numPeople).str());

Avec ce code C++ 03, il y a une probabilité d'allocations de mémoire dynamique en double et d'une copie de contenu, donc la construction suivie par le streaming était un meilleur pari.

31
Tony Delroy

Le stringbuf doit être initialisé avant de pouvoir y ajouter des valeurs. stringstream est un objet.

En faisant:

std::stringstream ss;

Vous autorisez essentiellement l'application à allouer de l'espace pour gérer les valeurs à ajouter.

0
dotslashb