C ++: chiamata di funzioni non membro con la stessa sintassi di quelle membro

5

Una cosa che vorrei fare in C ++ è chiamare le funzioni non membro con la stessa sintassi che chiamate funzioni membro:

class A { };
void f( A & this ) { /* ... */ }
// ...
A a;
a.f(); // this is the same as f(a);

Ovviamente questo può funzionare solo fino a

  • f non è virtuale (poiché non può apparire nella tabella virtuale di A .

  • f non ha bisogno di accedere ai membri non pubblici di A .

  • f non è in conflitto con una funzione dichiarata in A ( A::f ).

Mi piacerebbe una sintassi del genere perché secondo me sarebbe abbastanza comodo e spingerebbe le buone abitudini:

  1. chiamare str.strip() su std::string (dove strip è una funzione definita dall'utente) sembrerebbe molto meglio di chiamare strip( str ); .

  2. Le classi
  3. la maggior parte delle volte (sempre?) forniscono alcune funzioni membro che non richiedono di essere membri (cioè: non sono virtuali e non usano membri non pubblici). Questo rompe l'incapsulamento, ma è la cosa più pratica da fare (a causa del punto 1).

La mia domanda qui è: cosa ne pensi di tale funzionalità? Pensi che sarebbe qualcosa di carino, o qualcosa che introdurrebbe più problemi di quelli che mira a risolvere? Potrebbe essere sensato proporre tale caratteristica allo standard successivo (quello dopo C ++ 0x)?

Naturalmente questa è solo una breve descrizione di questa idea; non è completo; probabilmente dovremmo contrassegnare esplicitamente una funzione con una parola chiave speciale per farlo funzionare così e molte altre cose.

    
posta peoro 17.01.2011 - 15:27
fonte

6 risposte

18

re il tuo # 2: in realtà, rendere le funzioni non membri spesso aumenta l'incapsulamento , come osservato da Scott Meyers più di un decennio fa.

Comunque, ciò che descrivi somiglia molto ai metodi di estensione di C #. Sono buoni per lenire le menti di coloro che sono spaventati quando vedono funzioni totalmente libere. :) Una volta affrontato lo STL (che, BTW, non è la libreria standard, ma solo quella parte di esso che proviene dall'STL originale) dove quasi tutte le funzioni sono così libere da non essere nemmeno funzioni reali, ma i modelli di funzioni non ti serviranno più.

In breve: Non provare a piegare una lingua per adattarti alla tua mentalità . Invece migliora la tua mentalità per abbracciare la filosofia della lingua. emergerai un po 'più alto dal farlo.

    
risposta data 17.01.2011 - 15:34
fonte
3

Cerca di rispondere ai tuoi punti:

  1. Questa è solo una questione di stile. A te suona meglio, a qualcun altro sembra peggio. Alcuni linguaggi (di eredità Lisp) definiscono tutte le chiamate come (<func name> <arguments>) , sono intrinsecamente migliori o peggiori di <func name>(<arguments>) ? Forse e forse no. Questione di stile Sono sicuro che nessuno prenderà in considerazione la possibilità di cambiare lo standard C ++ su tali considerazioni.
  2. Questo è ciò che i membri static sono per in C ++ - hai familiarità con questo costrutto? Inoltre, come dimostrano le librerie standard C ++ (le relative porzioni STL), è possibile combinare oggetti con funzioni non membri che agiscono su di essi in modo elegante.
risposta data 17.01.2011 - 15:31
fonte
2

Credo che come parte di Concepts (che sono stati rilasciati da C ++ 0x), c'era qualcosa di simile, ma andando nella direzione opposta, in modo che la sintassi non membro potesse essere usata per chiamare funzioni membro. In altre parole, dato un std::vector<int> vec , questo

begin(vec)

equivarrebbe a chiamare esplicitamente la funzione membro:

vec.begin();

Penso che sia un approccio migliore, in quanto è prevenuto in modo che preferisca le funzioni non membro, che sono la parte più "pubblica" dell'interfaccia. In una certa misura, le funzioni membro sono dettagli dell'implementazione che è probabile che cambino più spesso delle funzioni non comuni di livello più alto che utilizzano la classe.

Quindi qualcosa del genere ci permetterebbe di usare la sintassi generale dei non membri per tutto, e mentre implicitamente ricadrebbe a chiamare una funzione membro se non viene trovata alcuna funzione non membro corrispondente, preferirebbe sempre la più pubblica , la funzione più incapsulata disponibile, che è quella non membro.

Naturalmente, in entrambi i modi è possibile interrompere silenziosamente il codice, ma consentire la sintassi non di membro per chiamare le funzioni membro manterrà almeno l'incapsulamento: aggiungendo una nuova funzione, posso convertire una chiamata di funzione membro, che ha accesso ai membri privati del classe, in un non membro che non ha accesso ai membri privati.

Ma se fosse stato fatto il contrario, permettendo la sintassi dei membri per chiamare funzioni non membro, allora potrei, aggiungendo una nuova funzione alla classe, convertire silenziosamente un numero sconosciuto di chiamate di funzione non membro nella funzione membro chiamate. In altre parole, un sacco di codice che in precedenza chiamava una funzione che non aveva accesso ai membri privati, viene silenziosamente modificato in chiamate a una funzione che fa accede ai membri privati. Penso che sia il modo più pericoloso di andare.

    
risposta data 17.01.2011 - 23:16
fonte
1

Se vuoi aggiungere metodi a una classe, eredita semplicemente da quella chiamata e aggiungi i membri alla tua classe derivata.

Se mi stai chiedendo come mi piace la tua sintassi, non lo so. Perché farlo apparire come un metodo fa parte di una classe quando non lo è. Soprattutto quando è abbastanza semplice farlo diventare parte di una classe.

    
risposta data 17.01.2011 - 15:33
fonte
1

Penso che sia un'ottima idea a causa dell'ADL e dell'operatore che sta sovraccaricando gli operatori pseudo-membri, abbandonando l'ADL. I metodi di estensione come questo non cambierebbero l'incapsulamento perché sono ancora una funzione esterna, hanno solo la sintassi di una funzione interna.

Ci sono alcune funzioni che fanno bene essere libere, e alcune funzioni che fanno bene ad essere membri, anche se sono libere da un punto di vista di incapsulamento.

    
risposta data 17.01.2011 - 15:38
fonte
0

Per me è un valore essere chiaramente in grado di vedere se sto facendo una chiamata a una funzione membro o non membro.

Ma anche se preferissi il tuo stile, e se dopo aver scritto una tale chiamata a.f(...) nel mio codice, una funzione f() con una firma compatibile è stata introdotta nella classe A ? Il mio codice dovrebbe interrompersi in silenzio sulla prossima ricompilazione, chiamando in una funzione completamente diversa da quella che inizialmente intendevo? Il comitato di progettazione dovrebbe tentare di estendere le regole di ricerca del nome (già molto complesse) per ospitare casi speciali? ... E se poi avrò già lasciato la compagnia nel frattempo, e spetterà al mio povero successore provare decidere quale potrebbe essere il mio intento originale?

    
risposta data 17.01.2011 - 15:35
fonte

Leggi altre domande sui tag