Quelle est la meilleure signature pour les opérateurs arithmétiques surchargés en C ++?
-
02-07-2019 - |
Question
J'avais supposé que la forme canonique de l'opérateur +, supposant l'existence d'un opérateur surchargé + = fonction membre, était comme ceci:
const T operator+(const T& lhs, const T& rhs)
{
return T(lhs) +=rhs;
}
Mais on m'a fait remarquer que cela fonctionnerait aussi:
const T operator+ (T lhs, const T& rhs)
{
return lhs+=rhs;
}
En substance, ce formulaire transfère la création du temporaire du corps de l'implémentation à l'appel de fonction.
Il semble un peu gênant d’avoir des types différents pour les deux paramètres, mais y at-il un problème avec la deuxième forme? Y a-t-il une raison de préférer l'une à l'autre?
La solution
Avec la question modifiée, le premier formulaire serait préféré. Le compilateur optimisera probablement la valeur de retour (vous pouvez le vérifier en plaçant un point d'arrêt dans le constructeur de T). La première forme prend également les deux paramètres comme const, ce qui serait plus souhaitable.
Recherche sur le thème de l'optimisation des valeurs de retour , tel que ce lien, par exemple: http://www.cs.cmu.edu/~gilpin/c++/performance.html
Autres conseils
Je ne suis pas sûr qu'il y ait beaucoup de différence dans le code généré pour l'un ou l'autre.
Entre ces deux, je préférerais (personnellement) la première forme, car elle traduit mieux l’intention. Ceci concerne à la fois votre réutilisation de l’opérateur + = et l’idée de passer des types modélisés par const & amp..
Je préférerais que le premier formulaire soit lisible.
J'ai dû réfléchir à deux fois avant de voir que le premier paramètre était copié. Je ne m'y attendais pas. Par conséquent, étant donné que les deux versions sont probablement aussi efficaces, je choisirais une version plus facile à lire.
const T operator+(const T& lhs, const T& rhs)
{
return T(lhs)+=rhs;
}
pourquoi pas cela si vous voulez la concision?
Ma première pensée est que la deuxième version pourrait être infiniment plus rapide que la première, car aucune référence n’est poussée sur la pile en tant qu’argument. Cependant, cela dépendrait beaucoup du compilateur et dépendrait par exemple du fait que le compilateur optimise ou non l'optimisation de la valeur de retour.
Quoi qu’il en soit, en cas de doute, ne choisissez jamais un gain de performance minime qui pourrait même ne pas exister et dont vous n’auriez probablement pas besoin - choisissez la version la plus claire, qui est la première.
En fait, la seconde est préférable. Comme indiqué dans la norme c ++,
3.7.2 / 2: Durée de stockage automatique
Si un objet automatique nommé a initialisation ou un destructeur avec effets secondaires, il ne doit pas être détruit avant la fin de son bloc, il ne sera pas non plus éliminé en tant que optimisation même s'il semble être non utilisé, sauf qu'un objet de classe ou sa copie peut être éliminée comme spécifié en 12.8.
En effet, comme un objet temporaire sans nom est créé à l'aide d'un constructeur de copie, le compilateur ne peut pas utiliser l'optimisation de la valeur de retour. Dans le second cas, toutefois, l'optimisation de la valeur de retour sans nom est autorisée. Notez que si votre compilateur implémente l'optimisation des valeurs de retour nommées, le meilleur code est
.const T operator+(const T& lhs, const T& rhs)
{
T temp(lhs);
temp +=rhs;
return temp;
}
Je pense que si vous les intégriez tous les deux (comme je le ferais, étant donné qu'ils ne font que transférer des fonctions et que la fonction operator + = () est probablement hors ligne), vous obtiendrez une génération de code presque indiscernable. Cela dit, le premier est plus canonique. La deuxième version est inutilement "mignonne".