web-dev-qa-db-fra.com

Comment surcharger correctement l'opérateur << pour un ostream?

J'écris une petite bibliothèque de matrices en C++ pour les opérations matricielles. Cependant mon compilateur se plaint, alors qu'avant il ne le faisait pas. Ce code a été laissé sur une étagère pendant 6 mois et entre les deux, j'ai mis à niveau mon ordinateur de debian etch à lenny (g ++ (Debian 4.3.2-1.1) 4.3.2), mais j'ai le même problème sur un système Ubuntu avec le même g ++. .

Voici la partie pertinente de ma classe de matrice:

namespace Math
{
    class Matrix
    {
    public:

        [...]

        friend std::ostream& operator<< (std::ostream& stream, const Matrix& matrix);
    }
}

Et la "mise en oeuvre":

using namespace Math;

std::ostream& Matrix::operator <<(std::ostream& stream, const Matrix& matrix) {

    [...]

}

C'est l'erreur donnée par le compilateur:

matrix.cpp: 459: erreur: 'std :: ostream & Math :: Matrix :: opérateur << (std :: ostream &, const Math :: Matrix &)' doit prendre exactement un argument

Cette erreur me laisse un peu perplexe, mais là encore, mon C++ est devenu un peu rouillé après avoir fait beaucoup de Java ces 6 mois. :-)

221

Vous avez déclaré votre fonction comme friend. Ce n'est pas un membre de la classe. Vous devez supprimer Matrix:: de l'implémentation. friend signifie que la fonction spécifiée (qui n'est pas membre de la classe) peut accéder aux variables de membre privées. La façon dont vous avez implémenté la fonction est comme une méthode d'instance pour la classe Matrix qui est incorrecte.

119
Mehrdad Afshari

Je vous parle simplement d'une autre possibilité: j'aime utiliser les définitions d'amis pour cela:

namespace Math
{
    class Matrix
    {
    public:

        [...]

        friend std::ostream& operator<< (std::ostream& stream, const Matrix& matrix) {
            [...]
        }
    };
}

La fonction sera automatiquement ciblée dans l'espace de nom environnant Math (même si sa définition apparaît dans l'étendue de cette classe), mais ne sera visible que si vous appelez l'opérateur << avec un objet Matrix qui permettra de rechercher une recherche dépendante de l'argument cette définition d'opérateur. Cela peut parfois aider avec des appels ambigus, puisqu'il est invisible pour les types d'argument autres que Matrix. Lors de l'écriture de sa définition, vous pouvez également vous référer directement aux noms définis dans Matrix et à Matrix lui-même, sans le qualifier avec un préfixe éventuellement long et en fournissant des paramètres de modèle tels que Math::Matrix<TypeA, N>.

134

Pour ajouter à Mehrdad répondre,

namespace Math
{
    class Matrix
    {
       public:

       [...]


    }   
    std::ostream& operator<< (std::ostream& stream, const Math::Matrix& matrix);
}

Dans votre mise en œuvre

std::ostream& operator<<(std::ostream& stream, 
                     const Math::Matrix& matrix) {
    matrix.print(stream); //assuming you define print for matrix 
    return stream;
 }
74
kal

En supposant qu'il s'agisse de surcharger operator << pour toutes les classes dérivées de std::ostream afin de gérer la classe Matrix (et de ne pas surcharger << pour Matrix classe), il est plus logique de déclarer la fonction de surcharge en dehors de l'espace de noms Math dans l'en-tête.

Utilisez une fonction amie uniquement si la fonctionnalité ne peut pas être obtenue via les interfaces publiques.

Matrix.h

namespace Math { 
    class Matrix { 
        //...
    };  
}
std::ostream& operator<<(std::ostream&, const Math::Matrix&);

Notez que la surcharge d'opérateur est déclarée en dehors de l'espace de noms.

Matrix.cpp

using namespace Math;
using namespace std;

ostream& operator<< (ostream& os, const Matrix& obj) {
    os << obj.getXYZ() << obj.getABC() << '\n';
    return os;
}

Par contre, si votre fonction de surcharge doit être faite à un ami, c’est-à-dire qu’elle doit avoir accès à des membres privés et protégés.

Math.h

namespace Math {
    class Matrix {
        public:
            friend std::ostream& operator<<(std::ostream&, const Matrix&);
    };
}

Vous devez inclure la définition de fonction avec un bloc d'espace de nom au lieu de simplement using namespace Math;.

Matrix.cpp

using namespace Math;
using namespace std;

namespace Math {
    ostream& operator<<(ostream& os, const Matrix& obj) {
        os << obj.XYZ << obj.ABC << '\n';
        return os;
    }                 
}
63
sanjivr

En C++ 14, vous pouvez utiliser le modèle suivant pour imprimer tout objet comportant un T :: print (std :: ostream &) const; membre.

template<class T>
auto operator<<(std::ostream& os, const T& t) -> decltype(t.print(os), os) 
{ 
    t.print(os); 
    return os; 
} 
32
QuentinUK