2009-07-14 12 views
17

Perché i parametri predefiniti devono essere aggiunti per ultimi nelle funzioni C++?Perché i parametri predefiniti devono essere aggiunti per ultimi nelle funzioni C++?

+12

Non è inutile. Comprendere la progettazione della lingua (e in particolare il design della lingua o delle lingue che usi) dovrebbe essere incoraggiata. Per fare questo, tra le altre cose è necessario scoprire perché le tue idee per miglioramenti "ovvi, semplici" sono poco pratici, se non sono pratici. E perché i designer di quella lingua hanno perso un'opportunità, se non sono pratici. Sono abbastanza sicuro che il design della lingua sia legato alla programmazione. –

risposta

26

Per semplificare la definizione della lingua e mantenere leggibile il codice.

void foo(int x = 2, int y); 

Per chiamare questo e sfruttare il valore di default, avreste bisogno di sintassi simile a questo:

foo(, 3); 

che era probabilmente sentiva di essere troppo strano. Un'altra alternativa è specificare i nomi nella lista degli argomenti:

foo(y : 3); 

Un nuovo simbolo dovrebbe essere utilizzato perché questo significa già qualcosa:

foo(y = 3); // assign 3 to y and then pass y to foo. 

L'approccio di denominazione è stata considerata e respinta dal comitato ISO perché erano a disagio nell'introdurre un nuovo significato per i nomi dei parametri al di fuori della definizione della funzione.

Se sei interessato a più razionali di progettazione in C++, leggi The Design and Evolution of C++ di Stroustrup.

+1

Perché non possiamo usare foo (3) invece di foo (, 3)? C'è qualche problema che impedisce al compilatore di capire l'intensione del programmatore? Penso che una ragione migliore potrebbe essere quella di ridurre le possibilità di ambiguità durante il tempo di compilazione e semplificare la costruzione dei compilatori. – user95319

+6

In D & E, Stroustrup dice che l'idioma dei parametri con nome è complicato in C++ a causa della separazione della dichiarazione di funzione e della definizione di funzione. Se l'utente usa un nome nella dichiarazione e un altro nell'implementazione, il compilatore dovrà trascurarne uno. – Abhay

+0

Abhay - Non penso che lo dica - penso che * riferisca * che alcune persone nel comitato lo abbiano pensato e ne spieghino le ragioni. Per quanto ne sappiamo, lo stesso Stroustrup avrebbe potuto fare una dipendenza dai nomi nella dichiarazione e ignorare la definizione (lo avrei). –

19

Se si definisce la seguente funzione:

void foo(int a, int b = 0, int c); 

Come vorresti chiamare la funzione e fornire un valore per A e C, ma lasciare b come predefinito?

foo(10, ??, 5); 

differenza di altri linguaggi (ad esempio, Python), argomenti di funzione in C/C++ non possono essere definiti in base al nome, come la seguente:

foo(a = 10, c = 5); 

Se ciò fosse possibile, allora gli argomenti di default potrebbe essere ovunque nella lista.

+0

riguardo ai casi che come sotto..foo (int a, double b = 0.0, char c = 'a') – yesraaj

+0

infatti, se il compilatore è abbastanza intelligente, dovremmo essere in grado di scrivere foo (10, 5). Il compilatore dovrebbe interpretarlo come foo (10, default, 5). – user95319

+0

@raj: consentito in C++. Puoi pensare ad esso come qualcosa di simile, una volta che hai iniziato a dare argomenti predefiniti devi dare fino alla fine (cioè fino all'ultimo parametro) senza lasciare nulla in mezzo. – Naveen

5

Immagina di avere una funzione con questo prototipo:

void testFunction(bool a = false, bool b = true, bool c); 

Ora supponiamo Ho chiamato la funzione in questo modo:

testFunction(true, false); 

Come è il compilatore dovrebbe capire quali parametri intendevo per la fornitura di valori per?

+0

Lo capirebbe perché ci sarebbero delle regole per questo. Ad esempio, gli argomenti si associano al secondo e al terzo parametro. – juanchopanza

1

Come regola generale, i parametri della funzione vengono elaborati dal compilatore e posizionati nello stack nell'ordine da destra a sinistra. Pertanto ha senso che tutti i parametri con valori predefiniti dovrebbero essere valutati per primi.

(applicato a __cdecl, che tende a essere il valore predefinito per VC++ e dichiarazioni di funzione __stdcall).

+5

Non è specificato per lo standard di lingua, quindi non può essere la ragione per cui funziona la lingua - si tratta di due livelli di preoccupazione completamente separati. –

+1

Ammetto che non me ne sono reso conto, ma so che è il portamento dei compilatori VC++ e gcc. – ChrisBD

1

È perché utilizza la posizione relativa degli argomenti per trovare a quali parametri corrispondono.

Potrebbe aver utilizzato i tipi per identificare che non è stato fornito un parametro opzionale. Ma la conversione implicita potrebbe interferire con esso. Un altro problema potrebbe essere rappresentato dagli errori di programmazione che potrebbero essere interpretati come argomenti facoltativi che si eliminano invece di errori di argomento mancanti.

Per consentire a qualsiasi argomento di diventare facoltativo, dovrebbe essere possibile identificare gli argomenti per assicurarsi che non vi siano errori di programmazione o rimuovere le ambiguità. Questo è possibile in alcune lingue, ma non in C++.

3

Come la maggior parte delle risposte evidenziano, i parametri predefiniti potenzialmente ovunque nell'elenco dei parametri aumentano la complessità e l'ambiguità delle chiamate di funzione (sia per il compilatore che, probabilmente, più importante per gli utenti della funzione).

Una cosa bella del C++ è che spesso c'è un modo per fare ciò che vuoi (anche se non è sempre una buona idea). Se si desidera avere argomenti predefiniti per le varie posizioni dei parametri, si può quasi certamente farlo scrivendo sovraccarichi che semplicemente girarsi e chiamano la funzione completamente parametrizzata in linea:

int foo(int x, int y); 
int foo(int y) { 
    return foo(0, y); 
} 

E il gioco è avere l'equivalente di:

int foo(int x = 0, int y); 
1

Un'altra cosa che il comitato degli standard doveva prendere in considerazione era il modo in cui i parametri predefiniti interagiscono con altre funzionalità, come funzioni sovraccaricate, risoluzione del modello e ricerca del nome. Queste caratteristiche interagiscono già in modi molto complessi e difficili da descrivere. Rendere i parametri di default in grado di apparire ovunque aumenterebbe la complessità.

0

Si tratta di convenzione di chiamata. Call Convention: Quando si chiama una funzione, i parametri vengono inseriti in pila da destra a sinistra. ad es.

fun(int a, int b, int c); 

lo stack è come questo: un b c modo, se si imposta il valore di default da sinistra a destra in questo modo:

fun(int a = 1, int b = 2, int c); 

e chiamare in questo modo:

fun(4,5); 

i mezzi di chiamata impostano a = 4, b = 5 e c = nessun valore; // che è sbagliato!

se si dichiara la funzione come questa:

fun(int a, int b = 2, int c = 3);

e chiamare in questo modo: fun(4, 5);

significa che il tuo chiamata impostato a = 4, b = 5, c = valore di default (3); // quale è giusto!

In conclusione, è necessario inserire il valore predefinito da destra a sinistra.

0

Jing Zeng è corretto. Vorrei aggiungere le mie osservazioni qui. Quando viene chiamata una funzione, gli argomenti vengono inseriti nello stack da destra a sinistra. Ad esempio, supponiamo tu abbia questa funzione arbitraria.

int add(int a, int b) { 
    int c; 
    c = a + b; 
    return c; 
} 

Ecco stack frame per la funzione:

------ 
    b 
------ 
    a 
------ 
ret 
------ 
    c 
------ 

Questo diagramma sopra è la stack frame per questa funzione! Come puoi vedere, la prima b viene spinta in pila, quindi una viene spinta in pila. Successivamente, l'indirizzo di ritorno della funzione viene inserito nello stack. L'indirizzo di ritorno della funzione mantiene la posizione in main() da dove la funzione è stata originariamente chiamata, e dopo che la funzione è stata eseguita, l'esecuzione del programma passa all'indirizzo di ritorno di quella funzione. Quindi qualsiasi variabile locale come c viene spinta in pila.

Ora la chiave è che gli argomenti vengono inseriti nello stack da destra a sinistra. Fondamentalmente, tutti i parametri predefiniti forniti sono valori letterali, che sono memorizzati nella sezione di codice di un eseguibile. Quando l'esecuzione del programma incontra un parametro predefinito senza un argomento corrispondente, spinge quel valore letterale in cima allo stack. Quindi guarda a e spinge il valore dell'argomento in cima allo stack. Il puntatore dello stack punta sempre in cima allo stack, la variabile più recente. Quindi tutti i valori letterali che hai inserito nello stack come parametri predefiniti sono "dietro" al puntatore dello stack.

Probabilmente è stato più efficiente per il compilatore inserire rapidamente inizialmente valori letterali predefiniti arbitrari nello stack, poiché non sono memorizzati in una posizione di memoria e creano rapidamente lo stack. Pensa a cosa sarebbe stato se le variabili fossero state prima inserite nello stack e poi quelle letterali. L'accesso a una posizione di memoria per la CPU richiede un tempo relativamente lungo rispetto alla rimozione di un valore letterale da un circuito o da un registro CPU. Poiché ci vuole più tempo per spingere le variabili nello stack rispetto ai letterali, i letterali dovrebbero aspettare, quindi l'indirizzo di ritorno dovrebbe aspettare e le variabili locali dovrebbero anche aspettare. Probabilmente non è una grande preoccupazione in termini di efficienza, ma questa è solo la mia teoria sul perché gli argomenti predefiniti sono sempre nelle posizioni più a destra dell'intestazione di una funzione in C++. Significa che il compilatore è stato progettato come tale.

Problemi correlati