J’ai rencontré ce problème lors du nettoyage des macros de débogage d’une ancienne application C / C ++: nous avons une classe Tracer héritant d’ ostrstream
(je sais qu’elle est obsolète depuis le C ++ 98, mais cette application a été écrite en 1998!). utiliser comme ceci:
Tracer() << "some" << " message" << " here";
Maintenant, si la première valeur de la chaîne est une chaîne constante comme ci-dessus, le résultat de l’appel de ostrstream::str()
sur le Tracer (effectué dans le destructeur, l’insertion du résultat dans une queue) contient une représentation hexadécimale du pointeur. à cette chaîne au lieu du texte. Ainsi, la déclaration ci-dessus produirait quelque chose comme "0x401a37 message here"
. Cela ne s’est pas produit avec les anciennes macros car elles avaient toujours un long (ID de thread) en tant que première valeur qui a maintenant été supprimée.
Entrer dedans avec gdb a montré que lors de la première insertion, cela appelait l’ operator<<(void const*)
sur le stream ostr, tandis que les insertions suivantes appelaient l’ operator<< (basic_ostream&, char const*)
(modèle supprimé pour plus de lisibilité).
Quelqu’un peut-il expliquer ce comportement? Quel serait un moyen propre à résoudre ce problème? J’ai trouvé une solution de contournement facile, qui utilise << left
comme premier argument – est-ce sûr? Y a-t-il de meilleures façons de le faire?
Voici un exemple réduit:
#include #include using namespace std; class Trace : public ostrstream { public: Trace(); virtual ~Trace(); }; Trace::Trace() : ostrstream() {} Trace::~Trace() { static_cast(*this) <<ends; char * text = ostrstream::str(); cout << "MESSAGE: "<< text <<endl; delete[] text; } int main(){ Trace() << "some" << " text" << " here"; Trace() << left << "some" << " text" << " here"; Trace() << 123 << " text" << " here"; }
Cela fonctionne de cette façon parce que Tracer()
est une valeur temporaire (rvalue) qui ne peut pas être liée à la référence non constante dans l’ operator<<(basic_ostream<...>&,
,.
Cependant, vous pouvez appeler des fonctions membres comme l’ operator<<(void const*)
, car cela ne nécessite pas de valeur lvalue.
La fonction membre renvoie ensuite une référence à l'object stream qui peut être utilisée pour appeler l' operator<<
suivant operator<<
dans la séquence.
L'appel de n'importe quel membre fonctionne de cette façon, comme Tracer() << left
ou Tracer() << flush
, et «converti» ainsi la référence en une référence lvalue est relativement sûre.
Si vous possédez un compilateur conforme à C ++ 11, la bibliothèque standard contient même un operator<<(basic_ostream<...>&&,
qui le fait pour vous. Dans ce cas, vous n'avez plus besoin de la solution de contournement.
Notons tout d’abord que l’ operator<<
qui prend const char*
comme argument est une fonction non membre. Et il existe une fonction membre qui prend void const*
comme argument.
Dans votre code, l'expression Trace() << "xyz"
peut uniquement appeler des fonctions membres, car Trace()
crée un délai, qui ne peut pas être lié au premier paramètre des fonctions de l' operator<<
non membre operator<<
, car ces fonctions prennent le premier argument comme std::ostream&
qui est une référence non-const. Donc, Trace() << "xyz"
résolu en operator<<
membre operator<<
qui prend void*
en argument, qui affiche l'adresse!
Mes conseils:
std::ostrstream
pas de la classe stream ( std::ostrstream
est de toute façon déconseillé ). operator<<
surcharge operator<<
Voici un exemple:
#include //for std::ossortingngstream struct Trace { std::ossortingngstream ss; template Trace& operator << (T const & data) { ss << data; return *this; } ~Trace() { std::cout << ss.str() << std::endl; } };
Maintenant, vous pouvez l'utiliser comme:
Trace() << "Hello World\n" << 100 << "\nBye\n";
Sortie:
Hello World 100 Bye
Démo en direct