Tous les processus de conception aboutissent à des compromis entre des objectifs incompatibles entre eux. Malheureusement, le processus de conception de l' &&
opérateur surchargé en C ++ a produit un résultat final déroutant: la fonctionnalité même que vous attendez &&
- son comportement de court-circuit - est omise.
Les détails de la façon dont ce processus de conception s'est terminé dans cet endroit malheureux, ceux que je ne connais pas. Il est cependant pertinent de voir comment un processus de conception ultérieur a pris en compte ce résultat désagréable. En C #, le surcharge &&
opérateur est court - circuit. Comment les concepteurs de C # ont-ils réalisé cela?
Une des autres réponses suggère le "levage lambda". C'est:
A && B
pourrait être réalisé comme quelque chose d'équivalent moral à:
operator_&& ( A, ()=> B )
où le deuxième argument utilise un mécanisme pour l'évaluation paresseuse de sorte que lorsqu'il est évalué, les effets secondaires et la valeur de l'expression sont produits. L'implémentation de l'opérateur surchargé ne ferait l'évaluation paresseuse que si nécessaire.
Ce n'est pas ce que l'équipe de conception C # a fait. (A part: bien que le levage lambda soit ce que j'ai fait quand est venu le temps de faire une représentation arborescente d'expression de l' ??
opérateur, ce qui nécessite que certaines opérations de conversion soient effectuées paresseusement. Décrire cela en détail serait cependant une digression majeure. Autant dire: levage lambda fonctionne mais est suffisamment lourd pour que nous souhaitions l'éviter.)
Au contraire, la solution C # décompose le problème en deux problèmes distincts:
- devrions-nous évaluer l'opérande de droite?
- si la réponse à ce qui précède était "oui", alors comment combiner les deux opérandes?
Par conséquent, le problème est résolu en rendant illégale la surcharge &&
directe. Au contraire, en C #, vous devez surcharger deux opérateurs, dont chacun répond à l'une de ces deux questions.
class C
{
// Is this thing "false-ish"? If yes, we can skip computing the right
// hand size of an &&
public static bool operator false (C c) { whatever }
// If we didn't skip the RHS, how do we combine them?
public static C operator & (C left, C right) { whatever }
...
(À part: en fait, trois. C # exige que si l'opérateur false
est fourni, l'opérateur true
doit également être fourni, ce qui répond à la question: est-ce que cette chose est "vraie"? nécessite les deux.)
Considérez un énoncé du formulaire:
C cresult = cleft && cright;
Le compilateur génère du code pour cela comme vous pensiez avoir écrit ce pseudo-C #:
C cresult;
C tempLeft = cleft;
cresult = C.false(tempLeft) ? tempLeft : C.&(tempLeft, cright);
Comme vous pouvez le voir, le côté gauche est toujours évalué. S'il est déterminé qu'il est "faux-ish", alors c'est le résultat. Sinon, le côté droit est évalué et l' opérateur désireux défini par l'utilisateur &
est appelé.
L' ||
opérateur est défini de manière analogue, comme une invocation de l'opérateur true et de l' |
opérateur impatient :
cresult = C.true(tempLeft) ? tempLeft : C.|(tempLeft , cright);
En définissant les quatre opérateurs - true
, false
, &
et |
- C # vous permet de dire non seulement , cleft && cright
mais aussi non court-circuit cleft & cright
, et aussi if (cleft) if (cright) ...
, et , c ? consequence : alternative
et while(c)
, et ainsi de suite.
Maintenant, j'ai dit que tous les processus de conception sont le résultat de compromis. Ici, les concepteurs de langage C # ont réussi à obtenir un court-circuit &&
et à ||
raison, mais cela nécessite de surcharger quatre opérateurs au lieu de deux , ce que certaines personnes trouvent déroutant. La fonctionnalité d'opérateur vrai / faux est l'une des fonctionnalités les moins bien comprises de C #. L'objectif d'avoir un langage sensé et simple qui est familier aux utilisateurs de C ++ a été opposé par le désir d'avoir des courts-circuits et le désir de ne pas implémenter le levage lambda ou d'autres formes d'évaluation paresseuse. Je pense que c'était une position de compromis raisonnable, mais il est important de se rendre compte qu'il est une position de compromis. Juste un différent position de compromis sur laquelle les concepteurs de C ++ ont atterri.
Si le sujet de la conception du langage pour de tels opérateurs vous intéresse, pensez à lire ma série sur les raisons pour lesquelles C # ne définit pas ces opérateurs sur des booléens nullables:
http://ericlippert.com/2012/03/26/null-is-not-false-part-one/
operator&&(const Foo& lhs, const Foo& rhs) : (lhs.bars == 0)