Mise stringstream temporaire c_str () en simple déclaration
-
19-09-2019 - |
Question
Considérons la fonction suivante:
void f(const char* str);
Supposons que je veuille générer une chaîne en utilisant stringstream et le transmettre à cette fonction. Si je veux le faire dans une déclaration, je pourrais essayer:
f((std::ostringstream() << "Value: " << 5).str().c_str()); // error
Cela donne une erreur: « str () » est pas un membre de « basic_ostream ». OK, l'opérateur << est de retour ostream au lieu de ostringstream - Que diriez-vous le jeter retour à un ostringstream
1) Est-ce casting sûr?
f(static_cast<std::ostringstream&>(std::ostringstream() << "Value: " << 5).str().c_str()); // incorrect output
Maintenant, avec cela, il tourne pour l'opérateur << ( « Valeur ») appel, il est en fait d'appeler l'opérateur de ostream << (void * le) et l'impression d'une adresse hexadécimale. Ceci est faux, je veux que le texte.
2) Pourquoi l'opérateur << sur la std :: ostringstream temporaire () appeler l'opérateur ostream? Certes, le temporaire a un type de 'ostringstream' non 'ostream'?
Je peux lancer le temporaire pour forcer trop l'appel de l'opérateur correct!
f(static_cast<std::ostringstream&>(static_cast<std::ostringstream&>(std::ostringstream()) << "Value: " << 5).str().c_str());
Cela semble fonctionner et passe "Valeur: 5" à f ()
.3) Est-ce que je comptais sur un comportement non défini maintenant? Les moulages semblent hors du commun.
Je suis au courant de la meilleure alternative est quelque chose comme ceci:
std::ostringstream ss;
ss << "Value: " << 5;
f(ss.str().c_str());
... mais je suis intéressé par le comportement de le faire en une seule ligne. Supposons que quelqu'un voulait faire un (douteux) macro:
#define make_temporary_cstr(x) (static_cast<std::ostringstream&>(static_cast<std::ostringstream&>(std::ostringstream()) << x).str().c_str())
// ...
f(make_temporary_cstr("Value: " << 5));
Est-ce que cette fonction comme prévu?
La solution
Vous ne pouvez pas lancer le flux temporaire std::ostringstream&
. Il est mal formé (le compilateur doit vous dire qu'il est faux). Ce qui suit peut le faire, bien que:
f(static_cast<std::ostringstream&>(
std::ostringstream().seekp(0) << "Value: " << 5).str().c_str());
Ce bien sûr est laid. Mais il montre comment cela peut fonctionner. seekp
est une fonction de renvoi d'un élément std::ostream&
. Serait sans doute préférable d'écrire cette manière générale
template<typename T>
struct lval { T t; T &getlval() { return t; } };
f(static_cast<std::ostringstream&>(
lval<std::ostringstream>().getlval() << "Value: " << 5).str().c_str());
La raison pour laquelle, sans tout ce qu'il faut le void*
, est parce que operator<<
est une fonction membre. Le operator<<
qui prend char const*
n'est pas.
Autres conseils
Un temporaire ne peut être passé comme une référence non-const à une fonction, c'est la raison pour laquelle il ne trouve pas l'opérateur de streaming correct et prend place celui avec vide l'argument * (il est fonction de membre et donc l'appeler sur une base temporaire est OK).
Ce qui vient à contourner les limites par coulée, j'ai le sentiment qu'il est en fait UB, mais je ne peux pas dire à coup sûr. Quelqu'un d'autre va sûrement citer la norme.
Cela peut être fait en utilisant une fonction C ++ 11 lambda.
#include <iostream>
#include <sstream>
void f(const char * str)
{
std::cout << str << std::endl;
}
std::string str(void (*populate)(std::ostream &))
{
std::ostringstream stream;
populate(stream);
return stream.str();
}
int main(int argc, char * * args)
{
f(str([](std::ostream & ss){ ss << "Value: " << 5; }).c_str());
return 0;
}
// g++ -std=c++11 main.cpp -o main
// ./main
// Value: 5
Si vous aimez les déclarations one_lined vous pouvez écrire:
// void f(const char* str);
f(static_cast<ostringstream*>(&(ostringstream() << "Value: " << 5))->str());
Cependant, vous devriez préférer plus facile à maintenir le code comme:
template <typename V>
string NumberValue(V val)
{
ostringstream ss;
ss << "Value: " << val;
return ss.str();
}
f(NumberValue(5));
J'utiliser quelque chose un peu comme ça pour l'exploitation forestière.
#include <sstream>
using namespace std;
const char *log_text(ostringstream &os, ostream &theSame)
{
static string ret; // Static so it persists after the call
ret = os.str();
os.str(""); // Truncate so I can re-use the stream
return ret.c_str();
}
int main(int argc, char **argv)
{
ostringstream ss;
cout << log_text(ss, ss << "My first message") << endl;
cout << log_text(ss, ss << "Another message") << endl;
}
Sortie:
Mon premier message
Un autre message