10 votes

Flux C++ comme paramètre lors de la surcharge de l'opérateur<<<

J'essaie d'écrire ma propre classe de journalisation et de l'utiliser comme un flux :

logger L;
L << "whatever" << std::endl;

Voici le code avec lequel j'ai commencé :

#include <iostream>

using namespace std;

class logger{
public:
    template <typename T>
    friend logger& operator <<(logger& log, const T& value);
};

template <typename T>
logger& operator <<(logger& log, T const & value) {
    // Here I'd output the values to a file and stdout, etc.
    cout << value;
    return log;
}

int main(int argc, char *argv[])
{
    logger L;
    L << "hello" << '\n' ; // This works
    L << "bye" << "alo" << endl; // This doesn't work
    return 0;
}

Mais j'ai obtenu une erreur en essayant de compiler, disant qu'il n'y avait pas de définition pour l'opérateur<< (en utilisant std::endl) :

pruebaLog.cpp:31: error: no match for ‘operator<<’ in ‘operator<< [with T = char [4]](((logger&)((logger*)operator<< [with T = char [4]](((logger&)(& L)), ((const char (&)[4])"bye")))), ((const char (&)[4])"alo")) << std::endl’

Donc, j'ai essayé de surcharger l'opérateur<< pour accepter ce genre de flux, mais ça me rend fou. Je ne sais pas comment faire. J'ai regardé, par exemple, la définition de std::endl dans le fichier d'en-tête ostream et j'ai écrit une fonction avec cet en-tête :

logger& operator <<(logger& log, const basic_ostream<char,char_traits<char> >& (*s)(basic_ostream<char,char_traits<char> >&))

Mais pas de chance. J'ai essayé la même chose en utilisant des modèles au lieu d'utiliser directement char, et j'ai aussi essayé d'utiliser simplement "const ostream& os", et rien.

Une autre chose qui me dérange est que, dans la sortie d'erreur, le premier argument pour l'opérateur<< change, parfois c'est une référence à un pointeur, parfois ressemble à une référence double....

9voto

Tyler McHenry Points 35551

endl est une bête étrange. Ce n'est pas une valeur constante. C'est en fait, entre toutes choses, une fonction. Vous avez besoin d'une surcharge spéciale pour gérer l'application de la fonction endl :

logger& operator<< (logger& log, ostream& (*pf) (ostream&))
{
  cout << pf;
  return log;
}

Cela accepte l'insertion d'une fonction qui prend un ostream et renvoie une référence ostream référence. C'est ce que endl est.

Edit : En réponse à l'intéressante question de FranticPedantic "pourquoi le compilateur ne peut-il pas déduire cela automatiquement ?". La raison est que si vous creusez encore plus profondément, endl est en fait lui-même un modèle fonction. Elle est définie comme suit :

template <class charT, class traits>
  basic_ostream<charT,traits>& endl ( basic_ostream<charT,traits>& os );

C'est-à-dire qu'il peut prendre n'importe quel type de ostream comme entrée et sortie. Donc le problème n'est pas que le compilateur ne peut pas déduire que T const & pourrait être un pointeur de fonction, mais qu'il n'arrive pas à déterminer dont endl que vous vouliez passer. La version modélisée de operator<< présentée dans la question accepterait un pointeur vers n'importe quelle fonction comme second argument, mais en même temps, la fonction endl représente un infini ensemble de fonctions potentielles, donc le compilateur ne peut rien faire de significatif à cet endroit.

Fournir la surcharge spéciale de l operator<< dont le second argument correspond à un spécifique instanciation de la endl permet à l'appel de se résoudre.

5voto

Jon Purdy Points 19408

endl est un manipulateur IO, c'est-à-dire un foncteur qui accepte un flux par référence, effectue une opération sur celui-ci, et renvoie ce flux, également par référence. cout << endl est équivalent à cout << '\n' << flushflush est un manipulateur qui vide le tampon de sortie.

Dans votre classe, il vous suffit d'écrire une surcharge pour cet opérateur :

logger& operator<<(logger&(*function)(logger&)) {
    return function(*this);
}

logger&(*)(logger&) est le type d'une fonction acceptant et retournant un logger par référence. Pour écrire vos propres manipulateurs, il suffit d'écrire une fonction qui correspond à cette signature, et de lui faire effectuer une opération sur le flux :

logger& newline(logger& L) {
    return L << '\n';
}

0voto

T.E.D. Points 26829

En C++, c'est le tampon de flux qui encapsule le mécanisme d'E/S sous-jacent. Le flux lui-même ne fait qu'encapsuler les conversions en chaînes de caractères et la direction des E/S.

Vous devriez donc utiliser l'une des classes de flux prédéfinies, plutôt que de créer la vôtre. Si vous avez une nouvelle cible vers laquelle vous souhaitez que vos E/S soient dirigées (comme un journal système), vous devez créer votre propre classe de flux tampon de flux (dérivé de std::streambuf ).

0voto

Mark B Points 60200

Je crois que le problème est que votre flux ne surcharge pas operator<< pour accepter une fonction qui a le même type que std::endl comme illustré dans cette réponse : std::endl est de type inconnu lors de la surcharge de l'opérateur<<

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X