Pourquoi l'opérateur flèche en C ++ n'est-il pas simplement un alias de *.?
18
En c ++, l'opérateur * peut être surchargé, comme avec un itérateur, mais l'opérateur flèche (->) (. *) Ne fonctionne pas avec les classes qui surchargent l'opérateur *. J'imagine que le préprocesseur pourrait facilement remplacer toutes les instances de -> par (* à gauche) .right, et cela rendrait les itérateurs plus agréables à implémenter. Y a-t-il une raison pratique pour -> d'être différent, ou est-ce juste une particularité de la langue / des concepteurs?
La règle qui foo->barvaut (*foo).barne vaut que pour les opérateurs intégrés.
Unary operator *n'a pas toujours la sémantique de déréférence du pointeur. Je pourrais créer une bibliothèque dans laquelle cela signifie une transposition matricielle, zéro ou plusieurs correspondances d'analyseur, ou à peu près n'importe quoi.
Cela rendrait le langage plus gênant si quelque chose qui surchargeait unaire operator *gagnerait soudainement un que operator ->vous n'avez pas demandé, avec une sémantique qui pourrait ne pas avoir de sens.
operator -> est surchargeable séparément, donc si vous en voulez un, vous pouvez en surcharger un avec un effort minimal.
Notez également qu'une telle surcharge aurait des propriétés plutôt intéressantes, comme le chaînage automatique des operator ->appels jusqu'à ce qu'un membre de la chaîne renvoie un pointeur brut. Ceci est très utile pour les pointeurs intelligents et autres types de proxy.
Qu'est-ce que votre exemple illustre? Renvoyez-vous un pointeur intelligent sur une chaîne et affichez-vous en quelque sorte la taille? Je suis confus.
Trevor Hickey
2
Il illustre le dernier paragraphe de ma réponse, comment utiliser les ->chaînes d'opérateurs jusqu'à ce qu'il obtienne un pointeur brut vers quelque chose, déréférencer et accéder à un membre de celui-ci. Si l'opérateur -> n'a pas enchaîné, l'exemple serait mal formé car un shared_ptr n'est pas un pointeur brut.
Lars Viklund
@LarsViklund: votre réponse a un problème: vous avez dit "opérateur-> ... enchaîne automatiquement l'opérateur-> appelle jusqu'à ce qu'un membre de la chaîne renvoie un pointeur brut". Ce n'est pas correct - en utilisant des A->Bchaînes de syntaxe au plus 1 appel supplémentaire. Ce que fait réellement la syntaxe binaire C ++ -> n'est pas d'appeler opeartor->directement l'objet - au lieu de cela, il regarde le type de Aet vérifie s'il s'agit d'un pointeur brut. Si c'est le ->déréfère et l'exécute B, sinon il appelle l'objet operator->, déréfie le résultat (soit en utilisant un pointeur brut natif ou un autre operator->, puis s'exécute Bsur le résultat
Guss
@Guss: Je ne trouve aucun chapitre et verset pour votre affirmation, ni ne le reproduis dans un compilateur. C ++ 11 13.5.6 / 1 indique que s'il existe une surcharge appropriée, x->mdoit être interprété comme (x.operator->())->m. Si le LHS est quelque chose qui a une surcharge appropriée de operator->nouveau, ce processus se répète jusqu'à ce qu'il y ait juste l' (*x).meffet habituel de 5.2.5 / 2.
Lars Viklund
8
"Le langage de programmation C ++" décrit le fait que ces opérateurs sont différents pour qu'ils puissent l'être, mais dit également:
Si vous fournissez plus d'un de ces opérateurs, il peut être judicieux de fournir l'équivalence, tout comme il est sage de garantir cela ++xet d' x+=1avoir le même effet que x=x+1pour une simple variable xd'une classe si ++, + =, = et + sont fournis.
Il semble donc que les concepteurs de langage aient fourni des points de surcharge distincts, car vous souhaiterez peut -être les surcharger différemment, plutôt que de supposer que vous souhaitez toujours qu'ils soient identiques.
En règle générale, C ++ est conçu pour favoriser la flexibilité, donc les surcharges *et ->sont distinctes. Bien qu'il soit assez inhabituel de le faire, si vous le voulez assez, vous pouvez écrire ces surcharges pour faire des choses entièrement différentes (par exemple, cela pourrait avoir du sens pour un langage spécifique au domaine implémenté à l'intérieur de C ++).
Cela dit, les itérateurs prennent en charge l'une ou l'autre utilisation. Sur les anciennes implémentations, vous pourriez trouver une bibliothèque qui nécessite (*iter).whateverau lieu de iter->whatever, mais si c'est le cas, c'est un bogue dans l'implémentation, pas une caractéristique du langage. Compte tenu de la quantité de travail impliquée dans la mise en œuvre de tous les conteneurs / algorithmes / itérateurs standard, il n'est pas surprenant que certaines versions préliminaires aient été quelque peu incomplètes, mais elles n'ont jamais vraiment été conçues de cette façon.
->
chaînes d'opérateurs jusqu'à ce qu'il obtienne un pointeur brut vers quelque chose, déréférencer et accéder à un membre de celui-ci. Si l'opérateur -> n'a pas enchaîné, l'exemple serait mal formé car un shared_ptr n'est pas un pointeur brut.A->B
chaînes de syntaxe au plus 1 appel supplémentaire. Ce que fait réellement la syntaxe binaire C ++ -> n'est pas d'appeleropeartor->
directement l'objet - au lieu de cela, il regarde le type deA
et vérifie s'il s'agit d'un pointeur brut. Si c'est le->
déréfère et l'exécuteB
, sinon il appelle l'objetoperator->
, déréfie le résultat (soit en utilisant un pointeur brut natif ou un autreoperator->
, puis s'exécuteB
sur le résultatx->m
doit être interprété comme(x.operator->())->m
. Si le LHS est quelque chose qui a une surcharge appropriée deoperator->
nouveau, ce processus se répète jusqu'à ce qu'il y ait juste l'(*x).m
effet habituel de 5.2.5 / 2."Le langage de programmation C ++" décrit le fait que ces opérateurs sont différents pour qu'ils puissent l'être, mais dit également:
Il semble donc que les concepteurs de langage aient fourni des points de surcharge distincts, car vous souhaiterez peut -être les surcharger différemment, plutôt que de supposer que vous souhaitez toujours qu'ils soient identiques.
la source
En règle générale, C ++ est conçu pour favoriser la flexibilité, donc les surcharges
*
et->
sont distinctes. Bien qu'il soit assez inhabituel de le faire, si vous le voulez assez, vous pouvez écrire ces surcharges pour faire des choses entièrement différentes (par exemple, cela pourrait avoir du sens pour un langage spécifique au domaine implémenté à l'intérieur de C ++).Cela dit, les itérateurs prennent en charge l'une ou l'autre utilisation. Sur les anciennes implémentations, vous pourriez trouver une bibliothèque qui nécessite
(*iter).whatever
au lieu deiter->whatever
, mais si c'est le cas, c'est un bogue dans l'implémentation, pas une caractéristique du langage. Compte tenu de la quantité de travail impliquée dans la mise en œuvre de tous les conteneurs / algorithmes / itérateurs standard, il n'est pas surprenant que certaines versions préliminaires aient été quelque peu incomplètes, mais elles n'ont jamais vraiment été conçues de cette façon.la source
(*i).m
est valide doit prendrei->m
en charge avec la même sémantique.