Partager via


Opérateurs d’affectation composée définie par l’utilisateur

Remarque

Cet article est une spécification de fonctionnalité. La spécification sert de document de conception pour la fonctionnalité. Elle inclut les changements de spécification proposés, ainsi que les informations nécessaires à la conception et au développement de la fonctionnalité. Ces articles sont publiés jusqu'à ce que les changements proposés soient finalisés et incorporés dans la spécification ECMA actuelle.

Il peut y avoir des divergences entre la spécification de la fonctionnalité et l'implémentation réalisée. Ces différences sont capturées dans les notes de réunion de conception de langage (LDM) pertinentes .

Vous pouvez en savoir plus sur le processus d’adoption des speclets de fonctionnalités dans la norme de langage C# dans l’article sur les spécifications.

Problème de champion : https://github.com/dotnet/csharplang/issues/9101

Résumé

Autoriser les types d’utilisateurs à personnaliser le comportement des opérateurs d’affectation composée de manière à ce que la cible de l’affectation soit modifiée sur place.

Motivation

C# prend en charge les implémentations d’opérateurs de surcharge pour les développeurs pour le type défini par l’utilisateur. En outre, il prend en charge les « opérateurs d’affectation composée » qui permettent à l’utilisateur d’écrire du code de la même façon que .x += yx = x + y Toutefois, le langage n’autorise pas actuellement le développeur à surcharger ces opérateurs d’affectation composée et alors que le comportement par défaut fait la bonne chose, en particulier lorsqu’il se rapporte aux types de valeurs immuables, il n’est pas toujours « optimal ».

Étant donné l’exemple suivant

class C1
{
    static void Main()
    {
        var c1 = new C1();
        c1 += 1;
        System.Console.Write(c1);
    }
    
    public static C1 operator+(C1 x, int y) => new C1();
}

avec les règles de langage actuelles, l’opérateur c1 += 1 d’affectation composée appelle l’opérateur défini par + l’utilisateur, puis affecte sa valeur de retour à la variable c1locale. Notez que l’implémentation de l’opérateur doit allouer et retourner une nouvelle instance de C1, tandis que, du point de vue du consommateur, une modification sur place de l’instance C1 d’origine fonctionne comme bon (elle n’est pas utilisée après l’affectation), avec un avantage supplémentaire d’éviter une allocation supplémentaire.

Lorsqu’un programme utilise une opération d’affectation composée, l’effet le plus courant est que la valeur d’origine est « perdue » et n’est plus disponible pour le programme. Avec les types qui ont des données volumineuses (telles que BigInteger, Tensors, etc.) le coût de production d’une nouvelle destination nette, d’itération et de copie de la mémoire a tendance à être assez coûteux. Une mutation sur place permettrait d’ignorer cette dépense dans de nombreux cas, ce qui peut apporter des améliorations significatives à ces scénarios.

Par conséquent, il peut être avantageux pour C# de permettre aux types d’utilisateurs de personnaliser le comportement des opérateurs d’affectation composée et d’optimiser les scénarios qui auraient autrement besoin d’allouer et de copier.

Conception détaillée

Syntaxe

La grammaire est https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/classes.md#15101-general ajustée comme suit.

Les opérateurs sont déclarés à l’aide de operator_declarations :

operator_declaration
    : attributes? operator_modifier+ operator_declarator operator_body
    ;

operator_modifier
    : 'public'
    | 'static'
    | 'extern'
    | unsafe_modifier   // unsafe code support
    | 'abstract'
    | 'virtual'
    | 'sealed'
+   | 'override'
+   | 'new'
+   | 'readonly'
    ;

operator_declarator
    : unary_operator_declarator
    | binary_operator_declarator
    | conversion_operator_declarator
+   | increment_operator_declarator
+   | compound_assignment_operator_declarator
    ;

unary_operator_declarator
    : type 'operator' overloadable_unary_operator '(' fixed_parameter ')'
    ;

logical_negation_operator
    : '!'
    ;

overloadable_unary_operator
-   : '+' | 'checked'? '-' | logical_negation_operator | '~' | 'checked'? '++' | 'checked'? '--' | 'true' | 'false'
+   : '+' | 'checked'? '-' | logical_negation_operator | '~' | 'true' | 'false'
    ;

binary_operator_declarator
    : type 'operator' overloadable_binary_operator
        '(' fixed_parameter ',' fixed_parameter ')'
    ;

overloadable_binary_operator
    : 'checked'? '+'  | 'checked'? '-'  | 'checked'? '*'  | 'checked'? '/'  | '%'  | '&' | '|' | '^'  | '<<'
    | right_shift | '==' | '!=' | '>' | '<' | '>=' | '<='
    ;

conversion_operator_declarator
    : 'implicit' 'operator' type '(' fixed_parameter ')'
    | 'explicit' 'operator' type '(' fixed_parameter ')'
    ;

+increment_operator_declarator
+   : type 'operator' overloadable_increment_operator '(' fixed_parameter ')'
+   | 'void' 'operator' overloadable_increment_operator '(' ')'
+   ;

+overloadable_increment_operator
+   : 'checked'? '++' | 'checked'? '--'
+    ;

+compound_assignment_operator_declarator
+   : 'void' 'operator' overloadable_compound_assignment_operator
+       '(' fixed_parameter ')'
+   ;

+overloadable_compound_assignment_operator
+   : 'checked'? '+=' | 'checked'? '-=' | 'checked'? '*=' | 'checked'? '/=' | '%=' | '&=' | '|=' | '^=' | '<<='
+   | right_shift_assignment
+   | unsigned_right_shift_assignment
+   ;

operator_body
    : block
    | '=>' expression ';'
    | ';'
    ;

Il existe cinq catégories d’opérateurs surchargés : opérateurs unaires, opérateurs binaires, opérateurs de conversion, opérateurs d’incrémentation, opérateurs d’assignation composée.

Les règles suivantes s’appliquent à toutes les déclarations d’opérateur :

  • Une déclaration d’opérateur doit inclure à la fois un modificateur et un publicstatic modificateur.

Les opérateurs d’affectation composée et d’incrément d’instance peuvent masquer les opérateurs déclarés dans une classe de base. Par conséquent, le paragraphe suivant n’est plus exact et doit être ajusté en conséquence, ou il peut être supprimé :

Étant donné que les déclarations d’opérateur nécessitent toujours la classe ou le struct dans lequel l’opérateur est déclaré pour participer à la signature de l’opérateur, il n’est pas possible qu’un opérateur déclaré dans une classe dérivée masque un opérateur déclaré dans une classe de base. Ainsi, le new modificateur n’est jamais requis et n’est donc jamais autorisé, dans une déclaration d’opérateur.

Opérateurs unaires

Voir https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/classes.md#15102-unary-operators.

Une déclaration d’opérateur doit inclure un static modificateur et ne doit pas inclure de override modificateur.

Le point de puce suivant est supprimé :

  • Un opérateur unaire ++ ou -- doit prendre un seul paramètre de type T ou T? et doit renvoyer ce même type ou un type dérivé de celui-ci.

Le paragraphe suivant est ajusté pour ne plus mentionner ++ et -- les jetons d’opérateur :

La signature d’un opérateur unaire se compose du jeton d’opérateur (+, , -!, ~, ++, --, true, ou false) et du type du paramètre unique. Le type de retour ne fait pas partie de la signature d’un opérateur unaire, ni le nom du paramètre.

Un exemple dans la section doit être ajusté pour ne pas utiliser d’opérateur d’incrément défini par l’utilisateur.

Opérateurs binaires

Voir https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/classes.md#15103-binary-operators.

Une déclaration d’opérateur doit inclure un static modificateur et ne doit pas inclure de override modificateur.

Opérateurs de conversion

Voir https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/classes.md#15104-conversion-operators.

Une déclaration d’opérateur doit inclure un static modificateur et ne doit pas inclure de override modificateur.

Opérateurs d’incrémentation

Les règles suivantes s’appliquent aux déclarations d’opérateur d’incrément statique, où T désigne le type d’instance de la classe ou du struct qui contient la déclaration d’opérateur :

  • Une déclaration d’opérateur doit inclure un static modificateur et ne doit pas inclure de override modificateur.
  • Un opérateur doit prendre un seul paramètre de type T ou T? retourner ce même type ou un type dérivé de celui-ci.

La signature d’un opérateur d’incrément statique se compose des jetons d’opérateur ('checked' ? ++, 'checked' ? --) et du type du paramètre unique. Le type de retour ne fait pas partie de la signature d’un opérateur d’incrément statique, ni le nom du paramètre.

Les opérateurs d’incrément statique sont très similaires aux opérateurs unaires.

Les règles suivantes s’appliquent aux déclarations d’opérateur d’incrément d’instance :

  • Une déclaration d’opérateur n’inclut pas de static modificateur.
  • Un opérateur ne prend aucun paramètre.
  • Un opérateur doit avoir void un type de retour.

En fait, un opérateur d’incrément d’instance est une méthode d’instance de retour void qui n’a aucun paramètre et a un nom spécial dans les métadonnées.

La signature d’un opérateur d’incrémentation d’instance se compose des tokens d’opérateur (« checked » ? « ++ » | « checked » ? « -- »).

Une checked operator déclaration nécessite une déclaration pair-sage d’un regular operator. Une erreur au moment de la compilation se produit sinon. Voir aussi .. /csharp-11.0/checked-user-defined-operators.md#semantics.

L’objectif de la méthode est d’ajuster la valeur de l’instance à la suite de l’opération d’incrément demandée, ce qui signifie dans le contexte du type déclarant.

Exemple:

class C1
{
    public int Value;

    public void operator ++()
    {
        Value++;
    }
}

Un opérateur d’incrément d’instance peut remplacer un opérateur par la même signature déclarée dans une classe de base, un override modificateur peut être utilisé à cet effet.

Les noms spéciaux « réservés » suivants doivent être ajoutés à la norme ECMA-335 afin de prendre en charge les versions d’instance des opérateurs d’incrémentation/décrémentation : | Nom | Opérateur | | -----| -------- | |op_DecrementAssignment| -- | |op_IncrementAssignment| ++ | |op_CheckedDecrementAssignment| checked -- | |op_CheckedIncrementAssignment| checked ++ |

Opérateurs d’affectation composée

Les règles suivantes s’appliquent aux déclarations d’opérateur d’affectation composée :

  • Une déclaration d’opérateur n’inclut pas de static modificateur.
  • Un opérateur doit prendre un paramètre.
  • Un opérateur doit avoir void un type de retour.

En fait, un opérateur d’assignation composée est une méthode d’instance de retour void qui accepte un paramètre et a un nom spécial dans les métadonnées.

La signature d’un opérateur d’affectation composé se compose des tokens d’opérateur (« checked » ? « += », « checked » ? « -= », « checked » ? « *= », « checked » ? « /= », « %= &», « = », « |= », « ^= », « <<= », right_shift_assignment, unsigned_right_shift_assignment) et du type du paramètre unique. Le nom du paramètre ne fait pas partie de la signature d’un opérateur d’affectation composée.

Une checked operator déclaration nécessite une déclaration pair-sage d’un regular operator. Une erreur au moment de la compilation se produit sinon. Voir aussi .. /csharp-11.0/checked-user-defined-operators.md#semantics.

L’objectif de la méthode est d’ajuster la valeur de l’instance au résultat de <instance> <binary operator token> parameter.

Exemple:

class C1
{
    public int Value;

    public void operator +=(int x)
    {
        Value+=x;
    }
}

Un opérateur d’assignation composée peut remplacer un opérateur par la même signature déclarée dans une classe de base, un override modificateur peut être utilisé à cet effet.

ECMA-335 a déjà « réservé » les noms spéciaux suivants pour les opérateurs d’incrémentation définis par l’utilisateur : | Nom | Opérateur | | -----| -------- | |op_AdditionAssignment|“+=” | |op_SubtractionAssignment|“-=” | |op_MultiplicationAssignment|“*=” | |op_DivisionAssignment|“/=” | |op_ModulusAssignment|“%=” | |op_BitwiseAndAssignment|“&=” | |op_BitwiseOrAssignment|“|=” | |op_ExclusiveOrAssignment|“^=” | |op_LeftShiftAssignment|“<<=”| |op_RightShiftAssignment| right_shift_assignment| |op_UnsignedRightShiftAssignment|unsigned_right_shift_assignment|

Toutefois, il indique que la conformité CLS exige que les méthodes d’opérateur soient des méthodes statiques non vides avec deux paramètres, c’est-à-dire qu’elles correspondent aux opérateurs binaires C#. Nous devrions envisager de assouplir les exigences de conformité CLS pour permettre aux opérateurs d’annuler le retour des méthodes d’instance avec un seul paramètre.

Les noms suivants doivent être ajoutés pour prendre en charge les versions vérifiées des opérateurs : | Nom | Opérateur | | -----| -------- | |op_CheckedAdditionAssignment| checked “+=” | |op_CheckedSubtractionAssignment| checked “-=” | |op_CheckedMultiplicationAssignment| checked “*=” | |op_CheckedDivisionAssignment| checked “/=” |

Opérateurs préfixés d’incrémentation et de décrémentation

Voir https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/expressions.md#1296-prefix-increment-and-decrement-operators

Si x in «op» x est classé comme variable et qu’une nouvelle version de langage est ciblée, la priorité est donnée aux opérateurs d’incrément d’instance comme suit.

Tout d’abord, une tentative est effectuée pour traiter l’opération en appliquant la résolution de surcharge d’opérateur d’incrément d’instance. Si le processus ne produit aucun résultat et aucune erreur, l’opération est traitée en appliquant la résolution de surcharge d’opérateur unaire comme https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/expressions.md#1296-prefix-increment-and-decrement-operators spécifié actuellement.

Sinon, une opération «op»x est évaluée comme suit.

Si le type d’un x type est connu comme étant un type de référence, il x est évalué pour obtenir une instance x₀, la méthode d’opérateur est appelée sur cette instance et x₀ est retournée à la suite de l’opération. Si x₀ c’est nullle cas, l’appel de méthode d’opérateur lève une exception NullReferenceException.

Par exemple:

var a = ++(new C()); // error: not a variable
var b = ++a; // var temp = a; temp.op_Increment(); b = temp; 
++b; // b.op_Increment();
var d = ++C.P1; // error: setter is missing
++C.P1; // error: setter is missing
var e = ++C.P2; // var temp = C.op_Increment(C.get_P2()); C.set_P2(temp); e = temp;
++C.P2; // var temp = C.op_Increment(C.get_P2()); C.set_P2(temp);

class C
{
    public static C P1 { get; } = new C();
    public static C P2 { get; set; } = new C();

    public static C operator ++(C x) => ...;
    public void operator ++() => ...;
}

Si le type de x type n’est pas connu pour être un type de référence :

  • Si le résultat de l’incrément est utilisé, il x est évalué pour obtenir une instance x₀, la méthode d’opérateur est appelée sur cette instance, x₀ est affectée x et x₀ est retournée à la suite de l’affectation composée.
  • Sinon, la méthode d’opérateur est appelée sur x.

Notez que les effets secondaires ne x sont évalués qu’une seule fois dans le processus.

Par exemple:

var a = ++(new S()); // error: not a variable
var b = ++S.P2; // var temp = S.op_Increment(S.get_P2()); S.set_P2(temp); b = temp;
++S.P2; // var temp = S.op_Increment(S.get_P2()); S.set_P2(temp);
++b; // b.op_Increment(); 
var d = ++S.P1; // error: set is missing
++S.P1; // error: set is missing
var e = ++b; // var temp = b; temp.op_Increment(); e = (b = temp); 

struct S
{
    public static S P1 { get; } = new S();
    public static S P2 { get; set; } = new S();

    public static S operator ++(S x) => ...;
    public void operator ++() => ...;
}

Opérateurs suffixés d’incrémentation et de décrémentation

Voir https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/expressions.md#12816-postfix-increment-and-decrement-operators

Si le résultat de l’opération est utilisé ou x qu’elle x «op» n’est pas classifiée comme variable ou qu’une ancienne version de langue est ciblée, l’opération est traitée en appliquant une résolution de surcharge d’opérateur unaire comme https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/expressions.md#12816-postfix-increment-and-decrement-operators spécifié actuellement. La raison pour laquelle nous n’essayons même pas les opérateurs d’incrément d’instance lorsque le résultat est utilisé, est le fait que, si nous traitons d’un type de référence, il n’est pas possible de produire une valeur avant x l’opération s’il est muté sur place. Si nous traitons un type valeur, nous devons faire des copies de toute façon, etc.

Sinon, la priorité est donnée aux opérateurs d’incrément d’instance comme suit.

Tout d’abord, une tentative est effectuée pour traiter l’opération en appliquant la résolution de surcharge d’opérateur d’incrément d’instance. Si le processus ne produit aucun résultat et aucune erreur, l’opération est traitée en appliquant la résolution de surcharge d’opérateur unaire comme https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/expressions.md#12816-postfix-increment-and-decrement-operators spécifié actuellement.

Sinon, une opération x«op» est évaluée comme suit.

Si le type de x type est connu comme étant un type de référence, la méthode d’opérateur est appelée sur x. Si x c’est nullle cas, l’appel de méthode d’opérateur lève une exception NullReferenceException.

Par exemple:

var a = (new C())++; // error: not a variable
var b = new C(); 
var c = b++; // var temp = b; b = C.op_Increment(temp); c = temp; 
b++; // b.op_Increment();
var d = C.P1++; // error: missing setter
C.P1++; // error: missing setter
var e = C.P2++; // var temp = C.get_P2(); C.set_P2(C.op_Increment(temp)); e = temp;
C.P2++; // var temp = C.get_P2(); C.set_P2(C.op_Increment(temp));

class C
{
    public static C P1 { get; } = new C();
    public static C P2 { get; set; } = new C();

    public static C operator ++(C x) => ...; 
    public void operator ++() => ...;
}

Si le type de x type n’est pas connu pour être un type de référence, la méthode d’opérateur est appelée sur x.

Par exemple:

var a = (new S())++; // error: not a variable
var b = S.P2++; // var temp = S.get_P2(); S.set_P2(S.op_Increment(temp)); b = temp;
S.P2++; // var temp = S.get_P2(); S.set_P2(S.op_Increment(temp));
b++; // b.op_Increment(); 
var d = S.P1++; // error: set is missing
S.P1++; // error: missing setter
var e = b++; // var temp = b; b = S.op_Increment(temp); e = temp; 

struct S
{
    public static S P1 { get; } = new S();
    public static S P2 { get; set; } = new S();

    public static S operator ++(S x) => ...; 
    public void operator ++() => ...;
}

Résolution de surcharge d’opérateur d’incrément d’instance

Une opération du formulaire «op» x ou x «op», où « op » est un opérateur d’incrément d’instance surchargé, et x est une expression de type X, est traitée comme suit :

  • L’ensemble d’opérateurs définis par l’utilisateur candidat fourni par X l’opération operator «op»(x) est déterminé à l’aide des règles des opérateurs d’incrément d’instance candidate.
  • Si l’ensemble d’opérateurs définis par l’utilisateur candidat n’est pas vide, cela devient l’ensemble d’opérateurs candidats pour l’opération. Sinon, la résolution de surcharge ne génère aucun résultat.
  • Les règles de résolution de surcharge sont appliquées à l’ensemble d’opérateurs candidats pour sélectionner le meilleur opérateur, et cet opérateur devient le résultat du processus de résolution de surcharge. Si la sélection de surcharge ne parvient pas à sélectionner un unique meilleur opérateur, une erreur de moment de liaison se produit.

Opérateurs d’incrément d’instance candidate

Étant donné un type T et une opération «op», où «op» est un opérateur d’incrément d’instance surchargé, l’ensemble d’opérateurs définis par l’utilisateur candidat fourni par T est déterminé comme suit :

  • Dans unchecked le contexte d’évaluation, il s’agit d’un groupe d’opérateurs qui seraient générés par le processus de recherche membre lorsque seuls les opérateurs d’instance operator «op»() étaient considérés comme correspondant au nom Ncible.
  • Dans checked le contexte d’évaluation, il s’agit d’un groupe d’opérateurs qui seraient générés par le processus de recherche de membre lorsque seuls les opérateurs d’instance et d’instance operator «op»()operator checked «op»() étaient considérés comme correspondant au nom Ncible. Les operator «op»() opérateurs qui ont des déclarations de correspondance appairées operator checked «op»() sont exclus du groupe.

Assignation composée

Voir https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/expressions.md#12214-compound-assignment

Le paragraphe au début qui traite dynamic est toujours applicable tel qu’il est.

Sinon, s’il xx «op»= y est classé comme variable et qu’une nouvelle version de langue est ciblée, la priorité est donnée aux opérateurs d’affectation composé comme suit.

Tout d’abord, une tentative est effectuée pour traiter une opération du formulaire x «op»= y en appliquant une résolution de surcharge d’opérateur d’affectation composée. Si le processus ne produit aucun résultat et aucune erreur, l’opération est traitée en appliquant la résolution de surcharge d’opérateur binaire comme https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/expressions.md#12214-compound-assignment spécifié actuellement.

Sinon, l’opération est évaluée comme suit.

Si le type de x type est connu comme étant un type de référence, il x est évalué pour obtenir une instance x₀, la méthode d’opérateur est appelée sur cette instance avec y comme argument et x₀ est retournée à la suite de l’affectation composée. Si x₀ c’est nullle cas, l’appel de méthode d’opérateur lève une exception NullReferenceException.

Par exemple:

var a = (new C())+=10; // error: not a variable
var b = a += 100; // var temp = a; temp.op_AdditionAssignment(100); b = temp; 
var c = b + 1000; // c = C.op_Addition(b, 1000)
c += 5; // c.op_AdditionAssignment(5);
var d = C.P1 += 11; // error: setter is missing
var e = C.P2 += 12; // var temp = C.op_Addition(C.get_P2(), 12); C.set_P2(temp); e = temp;
C.P2 += 13; // var temp = C.op_Addition(C.get_P2(), 13); C.set_P2(temp);

class C
{
    public static C P1 { get; } = new C();
    public static C P2 { get; set; } = new C();

    // op_Addition
    public static C operator +(C x, int y) => ...;

    // op_AdditionAssignment
    public void operator +=(int y) => ...;
}

Si le type de x type n’est pas connu pour être un type de référence :

  • Si le résultat de l’affectation composée est utilisé, il x est évalué pour obtenir une instance x₀, la méthode d’opérateur est appelée sur cette instance avec y comme argument, x₀ est affectée x et x₀ est retournée à la suite de l’affectation composée.
  • Sinon, la méthode d’opérateur est appelée avec xy comme argument.

Notez que les effets secondaires ne x sont évalués qu’une seule fois dans le processus.

Par exemple:

var a = (new S())+=10; // error: not a variable
var b = S.P2 += 100; // var temp = S.op_Addition(S.get_P2(), 100); S.set_P2(temp); b = temp;
S.P2 += 100; // var temp = S.op_Addition(S.get_P2(), 100); S.set_P2(temp);
var c = b + 1000; // c = S.op_Addition(b, 1000)
c += 5; // c.op_AdditionAssignment(5); 
var d = S.P1 += 11; // error: setter is missing
var e = c += 12; // var temp = c; temp.op_AdditionAssignment(12); e = (c = temp); 

struct S
{
    public static S P1 { get; } = new S();
    public static S P2 { get; set; } = new S();

    // op_Addition
    public static S operator +(S x, int y) => ...;

    // op_AdditionAssignment
    public void operator +=(int y) => ...;
}

Résolution de surcharge d’opérateur d’affectation composée

Une opération du formulaire x «op»= y, où «op»= est un opérateur d’assignation composée surchargée, x est une expression de type X traitée comme suit :

  • L’ensemble d’opérateurs définis par l’utilisateur candidat fourni par X l’opération operator «op»=(y) est déterminé à l’aide des règles des opérateurs d’affectation composée candidate.
  • Si au moins un opérateur défini par l’utilisateur candidat dans l’ensemble est applicable à la liste (y)d’arguments, il devient alors l’ensemble d’opérateurs candidats pour l’opération. Sinon, la résolution de surcharge ne génère aucun résultat.
  • Les règles de résolution de surcharge sont appliquées à l’ensemble d’opérateurs candidats pour sélectionner le meilleur opérateur par rapport à la liste (y)d’arguments, et cet opérateur devient le résultat du processus de résolution de surcharge. Si la sélection de surcharge ne parvient pas à sélectionner un unique meilleur opérateur, une erreur de moment de liaison se produit.

Opérateurs d’affectation composée candidate

Étant donné un type T et une opération «op»=, où «op»= est un opérateur d’affectation composée surchargée, l’ensemble d’opérateurs définis par l’utilisateur candidat fourni par T est déterminé comme suit :

  • Dans unchecked le contexte d’évaluation, il s’agit d’un groupe d’opérateurs qui seraient générés par le processus de recherche membre lorsque seuls les opérateurs d’instance operator «op»=(Y) étaient considérés comme correspondant au nom Ncible.
  • Dans checked le contexte d’évaluation, il s’agit d’un groupe d’opérateurs qui seraient générés par le processus de recherche de membre lorsque seuls les opérateurs d’instance et d’instance operator «op»=(Y)operator checked «op»=(Y) étaient considérés comme correspondant au nom Ncible. Les operator «op»=(Y) opérateurs qui ont des déclarations de correspondance appairées operator checked «op»=(Y) sont exclus du groupe.

Questions ouvertes

[Résolu] Le modificateur readonly devrait-il être autorisé dans les structures ?

Il semble qu’il n’y ait aucun avantage dans l’autorisation de marquer une méthode avec readonly quand l’objectif entier de la méthode est de modifier l’instance.

Conclusion: Nous allons autoriser readonly les modificateurs, mais nous n’assouplirons pas les exigences cibles pour l’instant.

[Résolu] Faut-il autoriser l’ombre ?

Si une classe dérivée déclare un opérateur « affectation composée » /« incrément d’instance » avec la même signature que celle d’une classe de base, faut-il exiger un override modificateur ?

Conclusion: L’ombre sera autorisée avec les mêmes règles que les méthodes.

[Résolu] Devrions-nous avoir une application de cohérence entre les opérateurs déclarés += et + ?

Pendant LDM-2025-02-12, une préoccupation a été soulevée au sujet des auteurs qui poussent accidentellement leurs utilisateurs dans des scénarios étranges où une += peut fonctionner, mais une + ne fonctionne pas (ou inversement), parce qu'une forme déclare plus d'opérateurs que l'autre.

Conclusion: Les vérifications ne seront pas effectuées sur la cohérence entre différentes formes d’opérateurs.

Alternatives

Continuer à utiliser des méthodes statiques

Nous pourrions envisager d’utiliser des méthodes d’opérateur statique où l’instance à muter est passée en tant que premier paramètre. Dans le cas d’un type valeur, ce paramètre doit être un ref paramètre. Sinon, la méthode ne pourra pas muter la variable cible. En même temps, en cas de type de classe, ce paramètre ne doit pas être un ref paramètre. Étant donné que dans le cas d’une classe, le passé dans l’instance doit être muté, et non l’emplacement où l’instance est stockée. Toutefois, lorsqu’un opérateur est déclaré dans une interface, il n’est souvent pas connu si l’interface sera implémentée uniquement par des classes ou uniquement par des structures. Par conséquent, il n’est pas clair si le premier paramètre doit être un ref paramètre.

Concevoir des réunions