2010-03-07 5 views
3

Considérons le code suivant:Question sur la surcharge opérateur +


class A 
{ 
public: 
    A& operator=(const A&); 
    const A& operator+(const A&); 
    const A& operator+(int m); 
}; 

int main() 
{ 
    A a; 
    a = (a + a) + 5; // error: binary '+' : no operator found which takes a left-hand operand of type 'const A' 
} 

Quelqu'un peut-il expliquer pourquoi ce qui précède est retourné comme une erreur?

"(a + a)" appelle "const A& operator+(const A&)" et renvoie une référence constante qui est ensuite passée à "const A& operator+(int m)" si je ne me trompe pas. Comment résoudre l'erreur ci-dessus (sans créer un opérateur binaire global + ou un constructeur acceptant un int) de telle sorte que l'instruction main() est autorisée?

+0

Merci pour toutes les explications. Je sais ci-dessus n'est pas la meilleure façon d'implémenter les opérateurs mais je me demandais juste pourquoi le compilateur se plaint dans le code ci-dessus. – jasonline

Répondre

7

qui est ensuite passé à « const Un opérateur & + (int m) » si je ne me trompe pas

Non, puisque la LHS est un const A& et ERS est un int, il appellera *

[anyType] operator+ (int rhs) const 
//       ^^^^^ note the const here. 

que vous avez seulement fourni les const non la version const A& operator+(int m), le compilateur se plaindra.

*: Ou operator+(const int& rhs) const ou operator+(float rhs) const ... Le point crucial est qu'il doit s'agir d'une méthode const.

+0

J'aime votre réponse, courte mais directe au but. – jasonline

6

operator+ doit retourner une instance, pas une référence:

// as member function 
A operator+(const A& other); 

// as free function 
A operator+(const A& left, const A& right); 

Pour expliquer le problème spécifique « renvoie une référence constante qui est ensuite transmis à const A& operator+(int m) ». Comme vous avez une référence const, elle ne peut pas appeler cette fonction car elle n'est pas une méthode const (c'est-à-dire const A& operator+(int m) const). Cela dit, ce n'est pas le moyen de corriger operator+. Si vous renvoyez une référence, à quoi fait-elle référence? Un opérateur local + serait mauvais car vous ne devriez pas renvoyer une référence à un local. Une référence à un global serait mauvaise car elle limiterait la façon dont votre code peut être utilisé correctement. Une référence à la mémoire allouée serait mauvaise car cela entraînera une fuite de mémoire. Une référence à *this serait mauvaise car alors operator+ agit comme operator +=.

+0

@R Samuel: Oui, je comprends. J'ai sorti cela d'un examen, c'est pourquoi je ne l'ai pas changé. – jasonline

+0

@jasonline: Si vous avez vraiment retiré cette merde d'un examen, alors, si vous le pouvez, abandonnez cet examen. Ce code est à peu près aussi mauvais que vous pouvez faire 'operator +', je vois très peu de place pour l'aggraver. – sbi

1

Le problème est que (a+a) retourne un soi-disant rvalue (essentiellement un terme de fantaisie pour un temporaire). Bien que vous puissiez appeler des fonctions membres sur un rvalue, vous ne pouvez appeler que les fonctions membres const. En outre, tout le monde a raison de dire que operator+ doit alwasys retourner une nouvelle valeur.

Vos opérateurs devraient être mis en œuvre comme ceci:

A operator+(const A&) const; 
A operator+(int m) const; 

Cependant, les opérateurs binaires qui ne modifient pas leur argument de gauche sont probablement mieux mis en œuvre en tant que fonctions libres:

class A { ... }; 

A operator+(const A& lhs, const A& rhs); 
A operator+(const A& lhs, int rhs); 

Habituellement, ils sont implémenté en plus de operator+=, qui est implémenté en tant que membre:

class A { 
    public: 
    A& operator+=(const A& rhs); 
    A& operator+=(int rhs); 
}; 

inline A operator+(A lhs, const A& rhs) // note: lhs is passed by copy now 
{ 
    lhs += rhs; 
    return lhs; 
} 
A operator+(A lhs, int rhs) // note: lhs is passed by copy now 
{ 
    lhs += rhs; 
    return lhs; 
} 
+0

@sbi: Merci pour l'info, oui je sais que le meilleur moyen devrait être de l'implémenter en tant que global et d'ajouter un constructeur prenant un int pour la conversion. Je n'ai juste pas compris la partie const c'est pourquoi. – jasonline

+0

@jasonline: Le meilleur moyen est généralement _not_ d'utiliser un constructeur de cast implicite, car cela nécessiterait la création d'un 'A' temporaire à partir d'un' int'. Habituellement, le meilleur moyen est de définir des surcharges d'opérateur distinctes pour le 'int' qui évitent la création de temporaire. (Si le constructeur est trivial et que tout le code impliqué est inline, le compilateur pourrait être capable d'éliminer le temporaire.) – sbi

+0

Vous voulez dire définir deux opérateurs surchargés où l'int est sur les lhs et l'autre où l'int est sur le rhs? Mais qu'est-ce qui ne va pas dans la création du temporaire dans ce cas? – jasonline

2

Parce que vous modifiez l'objet de gauche lors de l'ajout. Vous ne pouvez pas faire cela avec un objet const. Prenez conseil de Samuel, car la manière idiomatique est de retourner une nouvelle copie des objets ajoutés.

+0

@AraK: Oui, je comprends qu'il devrait retourner un nouvel objet. Ce code a été pris à partir d'un examen, donc je n'ai pas pris la peine de le changer. – jasonline

+0

@jasonline: Etes-vous sûr que l'examen n'a pas implémenté '+ =' de cette façon? Il est très difficile d'imaginer que n'importe quel examen aurait tort. – sbi

+0

@sbi: Je suppose que l'examen essayait juste de me tester si je pouvais identifier quelle partie causait l'erreur. Pas vraiment essayer de l'implémenter de la bonne façon. – jasonline

1

La fonction doit être const:

const A& operator+(int m) const;

1

Comme const A& operator+(const A&) renvoie une référence const fonction de membre non-const const A& operator+(int m) ne peut pas être appelé sur un objet const. Soit le premier opérateur doit être défini comme A& operator+(const A&) ou, le deuxième opérateur comme const A& operator+(int m)const; Cependant, ces changements ne les rendront techniquement correctes, pas esthétiquement dans la majorité des cas, car un opérateur binaire n'est pas censé modifier l'un des arguments d'entrée et encore calculer un résultat et retourner. Ainsi, le résultat doit être retourné par valeur ou dans le cas de C++ 0x, comme une référence de valeur r. , c'est-à-dire A operator+(const A& rhs)const ou A&& operator+(const A& rhs)const;

Questions connexes