web-dev-qa-db-fra.com

valeur de retour de la surcharge de l'opérateur en C ++

J'ai une question sur la valeur de retour de la surcharge d'opérateur en C++. En règle générale, j'ai trouvé deux cas, un retour par valeur et un retour par référence. Alors, quelle est la règle sous-jacente à cela? Surtout dans le cas où vous pouvez utiliser l'opérateur en continu, comme cout<<x<<y.

Par exemple, lors de l'implémentation d'une opération + "chaîne + (chaîne)". comment retourneriez-vous la valeur de retour, par ref ou par val.

45
skydoor

Certains opérateurs renvoient par valeur, certains par référence. En général, un opérateur dont le résultat est une nouvelle valeur (comme +, -, etc.) doit renvoyer la nouvelle valeur par valeur, et un opérateur dont le résultat est une valeur existante, mais modifiée (comme <<, >>, + =, - =, etc.), doit renvoyer une référence à la valeur modifiée.

Par exemple, cout est un std::ostream, et l'insertion de données dans le flux est une opération de modification, donc pour implémenter << opérateur à insérer dans un ostream, l'opérateur est défini comme ceci:

std::ostream& operator<< (std::ostream& lhs, const MyType& rhs)
{
  // Do whatever to put the contents of the rhs object into the lhs stream
  return lhs;
}

De cette façon, lorsque vous avez une instruction composée comme cout << x << y, la sous-expression cout << x est évalué en premier, puis l'expression [result of cout << x ] << y est évalué. Puisque l'opérateur << on x renvoie une référence à cout, l'expression [result of cout << x ] << y est équivalent à cout << y, comme prévu.

Inversement, pour "chaîne + chaîne", le résultat est une nouvelle chaîne (les deux chaînes d'origine sont inchangées), elle doit donc renvoyer par valeur (sinon vous renverriez une référence à un temporaire, ce qui n'est pas un comportement défini).

67
Tyler McHenry

Pour tenter de répondre à votre question concernant les chaînes, l'opérateur + () pour les chaînes est presque toujours implémenté en tant que fonction libre (non membre) afin que des conversions implicites puissent être effectuées sur l'un ou l'autre paramètre. C'est ainsi que vous pouvez dire des choses comme:

string s1 = "bar";
string s2 = "foo" + s1;

Étant donné que, et que nous pouvons voir qu'aucun des paramètres ne peut être modifié, il doit être déclaré comme:

RETURN_TYPE operator +( const string & a, const string & b );

Nous ignorons le RETURN_TYPE pour le moment. Comme nous ne pouvons retourner aucun paramètre (car nous ne pouvons pas les modifier), l'implémentation doit créer une nouvelle valeur concaténée:

RETURN_TYPE operator +( const string & a, const string & b ) {
    string newval = a;
    newval += b;    // a common implementation
    return newval;
}

Maintenant, si nous faisons de RETURN_TYPE une référence, nous retournerons une référence à un objet local, qui est un non-non bien connu car l'objet local n'existe pas en dehors de la fonction. Notre seul choix est donc de renvoyer une valeur, c'est-à-dire une copie:

string operator +( const string & a, const string & b ) {
    string newval = a;
    newval += b;    // a common implementation
    return newval;
}
12
anon

Si vous voulez que la surcharge de votre opérateur se comporte comme l'opérateur intégré, la règle est assez simple; la norme définit exactement comment les opérateurs intégrés se comportent et indiquera si le résultat d'un intégré est un rvalue ou un lvalue.

La règle que vous devez utiliser est:

  • si l'opérateur intégré retourne un rvalue alors votre surcharge doit retourner une référence
  • si la fonction intégrée renvoie un lvalue, votre surcharge doit renvoyer une valeur

Cependant, votre surcharge n'est pas requise pour renvoyer le même type de résultat que le résultat intégré, mais c'est ce que vous devez faire, sauf si vous avez une bonne raison de faire autrement.

Par exemple, KennyTM a noté dans un commentaire à une autre réponse que le flux surcharge pour le << et >> les opérateurs renvoient une référence à l'opérande de gauche, ce qui n'est pas ainsi que fonctionnent les fonctions intégrées. Mais les concepteurs de l'interface de flux l'ont fait pour que les E/S de flux puissent être enchaînées.

7
Michael Burr

Selon l'opérateur, vous devrez peut-être retourner par valeur.

Lorsque les deux peuvent être utilisés, comme dans l'opérateur + =, vous pouvez considérer les éléments suivants:

  • Si vos objets sont immuables, il est probablement préférable de revenir en valeur.
  • Si vos objets sont mutables, il est probablement préférable de revenir par référence.
3
Brian R. Bondy

Habituellement, vous retournez par référence dans une opération qui modifie la valeur des éléments sur lesquels elle opère, comme = ou +=. Toutes les autres opérations sont renvoyées par valeur.

C'est plus une règle d'or, cependant. Vous pouvez concevoir votre opérateur de toute façon.

3
thebretness