Perché l'operatore freccia in C ++ non è solo un alias di *.?


18

In c ++, l'operatore * può essere sovraccaricato, ad esempio con un iteratore, ma l'operatore freccia (->) (. *) Non funziona con le classi che sovraccaricano l'operatore *. Immagino che il preprocessore possa facilmente sostituire tutte le istanze di -> con (* left) .right, e ciò renderebbe gli iteratori più facili da implementare. c'è un motivo pratico per -> essere diversi o è solo una peculiarità del linguaggio / dei designer?

Risposte:


16

La regola foo->baruguale è (*foo).barvalida solo per gli operatori integrati.

Unary operator *non ha sempre la semantica di dereference puntatore. Potrei creare una libreria in cui significhi trasposizione matriciale, zero o più corrispondenze di parser o praticamente qualsiasi cosa.

Renderebbe il linguaggio più fastidioso se qualcosa che sovraccarichi unario operator *improvvisamente guadagnasse qualcosa che operator ->non chiedevi, con una semantica che potrebbe non avere senso.

operator -> è sovraccaricabile separatamente, quindi se lo desideri, puoi sovraccaricarne uno con il minimo sforzo.

Si noti inoltre che un tale sovraccarico avrebbe alcune proprietà piuttosto interessanti, come il concatenamento automatico delle operator ->chiamate fino a quando uno nella catena non restituisce un puntatore non elaborato. Questo è abbastanza utile per i puntatori intelligenti e altri tipi di proxy.

#include <boost/make_shared.hpp>
#include <boost/shared_ptr.hpp>
#include <string>
#include <iostream>
#include <ostream>

struct Foo
{
    boost::shared_ptr<std::string> operator -> () const
    {
        return boost::make_shared<std::string>("trololo");
    }
};

int main()
{
    Foo foo;
    std::cerr << foo->size() << std::endl;
}

Cosa illustra il tuo esempio? Stai restituendo un puntatore intelligente a una stringa e in qualche modo producendo le dimensioni? Non ho capito bene.
Trevor Hickey,

2
Illustra l'ultimo paragrafo della mia risposta, come usare le ->catene di operatori fino a quando non ottiene un puntatore grezzo a qualcosa, dereferenziare e accedere a un membro di esso. Se l'operatore -> non avesse una catena, l'esempio sarebbe mal formato poiché shared_ptr non è un puntatore non elaborato.
Lars Viklund,

@LarsViklund: la tua risposta ha un problema: hai detto "operatore-> ... incatena automaticamente l'operatore-> chiamate fino a quando uno nella catena non restituisce un puntatore non elaborato". Ciò non è corretto: l'utilizzo di A->Bcatene di sintassi al massimo 1 chiamata aggiuntiva. Quello che fa la C ++ -> sintassi binaria in realtà non è chiamare opeartor->direttamente l'oggetto - invece guarda il tipo di Ae controlla se si tratta di un puntatore non elaborato. Se quindi lo ->deref ed esegue Bsu quello, altrimenti chiama l'oggetto operator->, deref il risultato (usando un puntatore raw nativo o un altro operator->e poi esegue Bil risultato
Guss

@Guss: non riesco a trovare nessun capitolo e verso per il tuo reclamo, né riprodurlo in un compilatore. C ++ 11 13.5.6 / 1 indica che se esiste un sovraccarico adeguato, x->mdeve essere interpretato come (x.operator->())->m. Se l'LHS è qualcosa con un sovraccarico adeguato di operator->nuovo, questo processo si ripete fino a quando non si ottiene il solito (*x).meffetto di 5.2.5 / 2.
Lars Viklund,

8

"Il linguaggio di programmazione C ++" descrive il fatto che questi operatori sono diversi in modo che possano essere, ma dice anche:

Se fornisci più di uno di questi operatori, potrebbe essere saggio fornire l'equivalenza, così come è saggio assicurarlo ++xe x+=1avere lo stesso effetto x=x+1di una semplice variabile xdi qualche classe se ++, + =, = = e + sono forniti.

Quindi sembra che i progettisti del linguaggio abbiano fornito punti di sovraccarico separati perché potresti volerli sovraccaricare in modo diverso, invece di supporre che tu li voglia sempre essere gli stessi.


7

Come regola generale, C ++ è progettato per favorire la flessibilità, quindi i sovraccarichi di *e ->sono separati. Anche se è abbastanza insolito farlo, se lo desideri abbastanza male puoi scrivere quei sovraccarichi per fare cose completamente diverse (ad esempio, potrebbe avere senso per un linguaggio specifico del dominio implementato all'interno di C ++).

Detto questo, iteratori fanno supportare sia l'utilizzo. Nelle implementazioni antiche, potresti trovare una libreria che richiede (*iter).whateverinvece di iter->whatever, ma in tal caso, si tratta di un bug nell'implementazione, non una caratteristica del linguaggio. Data la quantità di lavoro necessaria per l'implementazione di tutti i contenitori / algoritmi / iteratori standard, non sorprende che alcune prime versioni fossero in qualche modo incomplete, ma non sono mai state pensate per essere così.


Non mi ero reso conto che i contenitori di libreria standard implementati -> o che fossero sovraccarichi.
Jakob Weisblat,

3
C ++ 03 24.1 / 1 richiede che qualsiasi iteratore (*i).mvalido sia supportato i->mcon la stessa semantica.
Lars Viklund,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.