Dichiaratori di funzioni C.
Prima di tutto, c'è C. In C, A a()è la dichiarazione di funzione. Ad esempio, putcharha la seguente dichiarazione. Normalmente, tali dichiarazioni vengono archiviate in file di intestazione, tuttavia nulla impedisce di scriverle manualmente, se si conosce l'aspetto della dichiarazione di funzione. I nomi degli argomenti sono facoltativi nelle dichiarazioni, quindi in questo esempio l'ho omesso.
int putchar(int);
Questo ti permette di scrivere il codice in questo modo.
int puts(const char *);
int main() {
puts("Hello, world!");
}
C consente inoltre di definire funzioni che accettano funzioni come argomenti, con una sintassi leggibile che assomiglia a una chiamata di funzione (beh, è leggibile, a patto che non si restituisca un puntatore alla funzione).
#include <stdio.h>
int eighty_four() {
return 84;
}
int output_result(int callback()) {
printf("Returned: %d\n", callback());
return 0;
}
int main() {
return output_result(eighty_four);
}
Come ho già detto, C consente di omettere i nomi degli argomenti nei file di intestazione, quindi output_resultsarebbe simile a questo nei file di intestazione.
int output_result(int());
Un argomento nel costruttore
Non lo riconosci? Bene, lascia che te lo ricordi.
A a(B());
Sì, è esattamente la stessa dichiarazione di funzione. Aè int, aè output_resulted Bè int.
Si può facilmente notare un conflitto di C con le nuove funzionalità di C ++. Per essere precisi, i costruttori sono il nome della classe e la parentesi e la sintassi della dichiarazione alternativa con ()anziché =. In base alla progettazione, C ++ cerca di essere compatibile con il codice C, e quindi deve occuparsi di questo caso, anche se praticamente a nessuno importa. Pertanto, le vecchie funzionalità C hanno la priorità sulle nuove funzionalità C ++. La grammatica delle dichiarazioni cerca di far corrispondere il nome come funzione, prima di tornare alla nuova sintassi con ()se fallisce.
Se una di queste funzionalità non esistesse o avesse una sintassi diversa (come {}in C ++ 11), questo problema non si sarebbe mai verificato per la sintassi con un argomento.
Ora potresti chiedere perché A a((B()))funziona. Bene, dichiariamo output_resulttra parentesi inutili.
int output_result((int()));
Non funzionerà La grammatica richiede che la variabile non sia tra parentesi.
<stdin>:1:19: error: expected declaration specifiers or ‘...’ before ‘(’ token
Tuttavia, C ++ prevede un'espressione standard qui. In C ++, puoi scrivere il seguente codice.
int value = int();
E il seguente codice.
int value = ((((int()))));
Il C ++ prevede che l'espressione all'interno delle parentesi sia ... beh ... espressione, al contrario del tipo C previsto. Le parentesi non significano nulla qui. Tuttavia, inserendo parentesi inutili, la dichiarazione della funzione C non viene abbinata e la nuova sintassi può essere abbinata correttamente (il che si aspetta semplicemente un'espressione, come 2 + 2).
Più argomenti nel costruttore
Sicuramente un argomento è carino, ma per quanto riguarda due? Non è che i costruttori possano avere solo un argomento. Una delle classi integrate che accetta due argomenti èstd::string
std::string hundred_dots(100, '.');
Questo va bene e va bene (tecnicamente, avrebbe parso più irritante se fosse scritto come std::string wat(int(), char()), ma siamo onesti - chi lo scriverebbe? Ma supponiamo che questo codice abbia un problema fastidioso. Supponiamo che tu debba mettere tutto tra parentesi.
std::string hundred_dots((100, '.'));
Non proprio così.
<stdin>:2:36: error: invalid conversion from ‘char’ to ‘const char*’ [-fpermissive]
In file included from /usr/include/c++/4.8/string:53:0,
from <stdin>:1:
/usr/include/c++/4.8/bits/basic_string.tcc:212:5: error: initializing argument 1 of ‘std::basic_string<_CharT, _Traits, _Alloc>::basic_string(const _CharT*, const _Alloc&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]’ [-fpermissive]
basic_string<_CharT, _Traits, _Alloc>::
^
Io non sono sicuro perché g ++ tentativi di convertire chara const char *. In entrambi i casi, il costruttore è stato chiamato con un solo valore di tipo char. Non c'è sovraccarico che ha un argomento di tipo char, quindi il compilatore è confuso. Potresti chiedere: perché l'argomento è di tipo char?
(100, '.')
Sì, ,ecco un operatore virgola. L'operatore virgola accetta due argomenti e fornisce l'argomento di destra. Non è davvero utile, ma è qualcosa che deve essere conosciuto per la mia spiegazione.
Invece, per risolvere l'analisi più irritante, è necessario il seguente codice.
std::string hundred_dots((100), ('.'));
Gli argomenti sono tra parentesi, non l'intera espressione. In effetti, solo una delle espressioni deve essere racchiusa tra parentesi, poiché è sufficiente uscire leggermente dalla grammatica C per utilizzare la funzione C ++. Le cose ci portano al punto di zero argomenti.
Zero argomenti nel costruttore
Potresti aver notato la eighty_fourfunzione nella mia spiegazione.
int eighty_four();
Sì, questo è influenzato anche dall'analisi più fastidiosa. È una definizione valida e molto probabilmente hai visto se hai creato i file di intestazione (e dovresti). L'aggiunta di parentesi non lo risolve.
int eighty_four(());
Perchè è così? Bene, ()non è un'espressione. In C ++, devi inserire un'espressione tra parentesi. Non puoi scrivere auto value = ()in C ++, perché ()non significa nulla (e anche se lo fosse, come una tupla vuota (vedi Python), sarebbe un argomento, non zero). In pratica ciò significa che non è possibile utilizzare la sintassi abbreviata senza utilizzare la sintassi di C ++ 11 {}, poiché non ci sono espressioni da mettere tra parentesi e si applicherà sempre la grammatica C per le dichiarazioni di funzione.
(B())è solo un'espressione C ++, niente di più. Non è alcun tipo di eccezione. L'unica differenza che fa è che non c'è modo che possa essere analizzato come un tipo, e quindi non lo è.