45 votes

N'est-ce pas le modificateur const ici inutile?

Le "Effective C++" l'Article 3 stipule que "l'Utilisation de const chaque fois que possible", et il donne un exemple:

const Rational operator*(const Rational& lhs, 
                            const Rational& rhs);

pour empêcher les clients d'être en mesure de commettre des atrocités comme ceci:

Rational a, b, c;
...
(a * b) = c;   // invoke operator= on the result of a*b!

Mais ce n'est pas la non-référence valeur de retour de fonctions déjà une rvalue? Pourquoi s'embêter à faire cela?

51voto

TemplateRex Points 26447

Le point est que pour les types de classe (mais pas pour builtin types), a = b est juste une abréviation pour a.operator=(b), où l'opérateur= est une fonction membre. Et des fonctions de membre peut être appelé sur rvalues ces (a * b) créé par Rational::operator*. Pour appliquer similaire sémantique que pour builtin rvalues ("faire comme les services de renseignements n'"), certains auteurs (Meyers y compris) a recommandé en C++98, pour les retours par const-rvalue pour les classes avec de tels opérateurs.

Toutefois, en C++11, retour par const-rvalue est une mauvaise idée car il va inhiber la sémantique de déplacement, car const rvalues ne peut pas se lier T&&.

Dans ses notes, Un aperçu de la nouvelle C++ (C++11), Scott Meyers donne exactement le même exemple de son vieux livre, et conclut qu'il est maintenant considéré comme une mauvaise conception pour ajouter la const valeur de retour. Le recommandé signature est maintenant

Rational operator*(const Rational& lhs, const Rational& rhs);

Mise à JOUR: Comme le sous-entend @JohannesSchaub-litb dans les commentaires, en C++11, vous pouvez également utiliser un qualificatif de référence sur l'opérateur d'affectation, de sorte qu'il n'acceptera que des lvalues comme sa gauche argument (c'est à dire l' *this pointeur, c'est pourquoi cette fonction est également connu comme "références rvalue pour *ce"). Vous aurez besoin de g++ >= 4.8.1 (vient de sortir) ou Clang >= 2,9 à en faire usage.

7voto

Andrew Durward Points 2012

Le modificateur const sur la valeur de retour n'est pas nécessaire et peut nuire à la sémantique de déplacement. Le meilleur moyen de prévention de la cession à rvalues en C++11 est d'utiliser des "ref-qualificatifs."

struct Rational
{
  Rational & operator=( Rational other ) &; // can only be called on lvalues
};

Rational operator*( Rational const & lhs, Rational const & rhs );

Rational a, b, c;

(a * b) = c; // error

5voto

Bathsheba Points 23209

Peut-être que cela va me coûter des points de rep, mais je suis en désaccord. Ne modifiez pas le rendement attendu des types d'opérateurs surchargés comme il va ennuyer les utilisateurs de votre classe. c'est à dire utiliser

Rational operator*(const Rational& lhs, const Rational& rhs);

(Bien sûr, consting les paramètres de bonnes pratiques, et ayant constante des paramètres de référence, c'est encore mieux car cela signifie que le compilateur ne va pas prendre profond de copies. Mais n'ont pas une référence constante de la valeur de retour dans ce cas, vous obtiendrez une balançant de référence qui est catastrophique. Mais notez que, parfois, la prise de référence est plus lent qu'un passage par valeur. Je pense qu' doubles et ints viennent dans cette catégorie, sur de nombreuses plateformes.)

3voto

Barmaley.exe Points 1743

Parce que vous pourriez être l'intention d'écrire (a * b) == c au lieu de cela c'est à dire

if ((a * b) = (c + d)) // executes if c+d is true

Mais vous avez voulu

if ((a * b) == (c + d)) // executes if they're equal

1voto

Alex Points 3973

Je suppose que ce que vous aimeriez faire en fonction de votre question est de déclarer le correspondant de l'opérateur= privé tel qu'il est ne sont plus accessibles.

d'où tu tiens à la surcharge de la signature qui correspond (a*b) = c. Je suis d'accord que la partie de gauche est une expression et, par conséquent, une rvalue serait un meilleur match. cependant, vous êtes en ignorant le fait que c'est la valeur de retour de la fonction si la surcharge de la fonction pour retourner une rvalue il le compilateur va se plaindre d'une défaillance de la surcharge surcharge règles ne considère pas les valeurs de retour.

Comme indiqué ici, la surcharge de l'opérateur d'affectation est toujours à l'intérieur de la définition de la classe. si il y aurait un non-membre de la signature comme void operator=(foo assignee, const foo& assigner); la résolution de surcharge pourrait correspondre à la première partie comme une rvalue (ensuite, vous pouvez le supprimer ou de le déclarer privé).

De sorte que vous pouvez choisir à partir de deux mondes:

  • vivre avec le fait que les utilisateurs peuvent écrire des trucs stupides comme (a*b) = c ce qui n'est pas mal, mais stocke la valeur de c dans un espace caché temporaire
  • utiliser la signature const foo operator*(const foo& lhs, const foo& rhs) qui dissallows l' (a*b) = c et le sacrifice d'une sémantique de déplacement

code

#include <utility>

class foo {
    public:
    foo() = default;
    foo(const foo& f) = default;
    foo operator*(const foo& rhs) const {
        foo tmp;
        return std::move(tmp);
    }
    foo operator=(const foo& op) const {
        return op;
    }
    private:
    // doesn't compile because overloading doesn't consider return values.
    // conflicts with foo operator=(const foo& op) const;
    foo && operator=(const foo& op) const; 
};


int main ( int argc, char **argv ) {
    foo t2,t1;
    foo t3 = t2*t1;
    foo t4;
    (t2 * t1) = t4;
    return 0;
}

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