Erreur si la surcharge d'un operateur n'est pas friend

Le problème exposé dans ce sujet a été résolu.

Bonjour à tous,

Je suis actuellement en train de suivre le cours " La programmation en C++ moderne" sur zds.

Lors du TP "Entrons dans la matrice !" j’obtiens une erreur que je ne comprends pas sur la surcharge de l’opérateur '+’.

Dans ce TP sur les matrices, la surcharge de l’opérateur '+' est une fonction amie de la class "Matrice".

    /**
     * @brief Opérateur libre d'addition de deux matrices.
     * @param[in] lhs La matrice de gauche. 
     * @param[in] rhs La matrice de droite.
     * @pre Les deux matrices doivent être de même dimension.
     * @return Le résultat de l'addition des deux matrices.
     */
    friend Matrice operator+(Matrice lhs, Matrice const & rhs);

Or dans le fichier "Matrice.cpp", l’implémentation de cette fonction ne requiert aucunes informations privées de la classe Matrice.

Matrice operator+(Matrice lhs, Matrice const & rhs)
{
    lhs += rhs;
    return lhs;
}

Donc pour moi, cette fonction n’a pas besoin d’être déclarée en tant que fonction amie. Néanmoins, sans cette déclaration (en tant que fonction friend de la classe Matrice) j’obtiens une erreur de compilation : error: no match for 'operator+' (operand types are 'Matrice' and 'Matrice').

Je ne comprends donc pas pourquoi cette surcharge d’opérateur doit-elle être déclarée en tant que friend de la classe Matrice.

J’espère que mon message est assez clair, s’il faut je rajoute plus mes .cpp et .hpp par la suite.

Je vous remercie !

Fxhi.

C’est pour une propriété détournée de l’amitié qu’on l’utilise pour les opérateurs binaires; cela en devient même une bonne pratique. En tant qu’amie cachée (hidden friend), lors de la résolution d’une addition sur une tierce classe pour laquelle operator+ n’a pas été déclaré, l’addition entre 2 matrices ne sera pas considérée.

https://gcc.godbolt.org/z/n4TnGnKh5 Mieux: https://gcc.godbolt.org/z/fKo63GKdM

Il y a d’autres propriétés relatives aux conversions implicites.

EDIT: J’avais lu trop vite. Maintenant, si la fonction est déclarée normalement dans la classe, alors elle devient une fonction membre et prend un paramètre de plus: l’objet courant. On se retrouve ainsi avec un opérateur d’addition d’arité 3, or cet opérateur est toujours binaire et jamais ternaire en C++. Sans friend, il faudrait sortir la déclaration de l’opérateur de la définition de la classe, comme ce que j’ai fait avec la classe Public dans l’exemple du dessus (suivre le lien vers le compilo en ligne godbolt)

Merci pour la réponse !

J’ai peut être mal compris (et aussi mal expliqué au début), mais ma déclaration de l’opérateur n’est pas dans la définition de la classe.

Voici mon "Matrice.hpp"

#ifndef Matrix_H
#define Matrix_H

#include <vector>
#include <tuple>

class Matrice
{
    public:
        Matrice() = delete;
        Matrice(int nb_row, int nb_column, int valeur = 0);
        Matrice(const Matrice& A);

        void const display();

        double const & operator()(int x, int y) const;
        double& operator()(int x, int y);

        std::tuple<int,int> size() const;

        Matrice& operator=(const Matrice& A);
        Matrice& operator+=(const Matrice& A);

        friend Matrice operator+(Matrice A, const Matrice& B);


    private:
        std::size_t offset(std::size_t ligne, std::size_t colonne) const noexcept;

        std::size_t m_nb_lignes;
        std::size_t m_nb_colonnes;
        std::vector<double> m_matrice;

};
#endif

et mon "Matrice.cpp"

#include "Matrice.hpp"

#include <iostream>
#include <cassert>
#include <vector>
#include <tuple>

Matrice::Matrice(int nb_row, int nb_column, int valeur) : m_nb_lignes(nb_row), m_nb_colonnes(nb_column)
{
    assert( ( nb_row > 0 || nb_column > 0 ) && "Number of row and column need to be > 0.");
    m_matrice.resize(nb_row * nb_column, valeur); 
}

Matrice::Matrice(const Matrice& A) 
    : m_nb_lignes(A.m_nb_lignes), m_nb_colonnes(A.m_nb_colonnes), m_matrice(A.m_matrice)
{
    // Nothing
}

// --- ---
// Member functions
// --- ---

std::size_t Matrice::offset(std::size_t ligne, std::size_t colonne) const noexcept
{
    return colonne * m_nb_lignes + ligne;
}

double const & Matrice::operator()(int x, int y) const
{
    return m_matrice[offset(x, y)];
}

double& Matrice::operator()(int x, int y)
{
    return m_matrice[offset(x, y)];
}

std::tuple<int,int> Matrice::size() const
{
    return std::make_tuple(m_nb_lignes, m_nb_colonnes);
}


void const Matrice::display()
{
    for (int i = 0; i < m_nb_lignes; i++)
    {
        for (int j = 0; j < m_nb_colonnes; j++)
        {
            std::cout << (*this)(i,j) << ' ';
        }
        std::cout << std::endl;
    }
}

Matrice& Matrice::operator=(const Matrice& A)
{
    m_nb_lignes = A.m_nb_lignes;
    m_nb_colonnes = A.m_nb_colonnes;
    m_matrice = A.m_matrice;
    return *this;
}

Matrice& Matrice::operator+=(const Matrice& A)
{
    assert( (*this).size() == A.size() && " Les matrices doivent avoir la même taille.");
    
    for (int i=0; i < m_nb_lignes; i++)
    {
        for (int j=0; j < m_nb_colonnes; j++)
        {
            (*this)(i,j) += A(i,j);
        }
    }
    return *this;
    
}

// --- ---
// Free functions
// --- ---

Matrice operator+(Matrice A, const Matrice& B)
{
    A += B;
    return A;
}

Sans le friend Matrice operator+(const Matrice& A, const Matrice& B); le code ne compile pas.

Alors que dans la section précédente du cours sur le C++ moderne, la surcharge de l’opérateur '+' pour la classe Fraction n’est pas définie comme friend :

class Fraction
{
public:
    Fraction& operator+=(Fraction const & fraction) noexcept;

private:
    int m_numerateur { 0 };
    int m_denominateur { 1 };
};

Fraction& Fraction::operator+=(Fraction const & fraction) noexcept
{
    m_numerateur = m_numerateur * fraction.m_denominateur + fraction.m_numerateur * m_denominateur;
    m_denominateur *= fraction.m_denominateur;
    return *this;
}

// L'argument lhs est recopié, donc on peut le modifier sans problème.
Fraction operator+(Fraction lhs, Fraction const & rhs) noexcept
{
    lhs += rhs;
    return lhs;
}
+0 -0

mais ma déclaration de l’opérateur n’est pas dans la définition de la classe. […] Sans le friend Matrice operator+(const Matrice& A, const Matrice& B); le code ne compile pas.

Il n’y a aucune raison pour que Matrice.cpp ne compile pas. Par contre, si tu additionnes 2 matrices dans un toto.cpp, alors ça sera normal. Il faut déclarer quelques part que l’addition existe. Je ne sais plus ce qui est dit au sujet du modèle de compilation particulier qui est hérité du C.

C’est exact, l’erreur apparaît dans mon main.cpp où j’additionne deux matrices.

Merci, cela provient bien du fait que je n’ai pas déclaré que l’addition existe ! Si je sépare ma classe Fraction en plusieurs fichiers (hpp et cpp) j’ai la même erreur.

Néanmoins, à part déclarer l’opérateur en tant que friend dans la classe associée, je n’ai pas trouvé d’autres solutions en ligne.

Il faut le déclarer comme d’hab comme pour les autres fonctions — cf l’exemple que j’avais donné plus sur godbolt (je n’avais même pas pris la peine de définir les fonctions en fait…). Voire carrément définir cette fonction inline dans le fichier d’en-tête.

Mais dans le cas particulier des opérateurs binaires, la déclaration amie est préférable d’un point de vu résolution des appels.

Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte