Déclarateurs de fonction C
Tout d'abord, il y a C. En C, A a()
c'est la déclaration de fonction. Par exemple, putchar
a la déclaration suivante. Normalement, ces déclarations sont stockées dans des fichiers d'en-tête, cependant rien ne vous empêche de les écrire manuellement, si vous savez à quoi ressemble la déclaration de fonction. Les noms d'argument sont facultatifs dans les déclarations, je l'ai donc omis dans cet exemple.
int putchar(int);
Cela vous permet d'écrire le code comme celui-ci.
int puts(const char *);
int main() {
puts("Hello, world!");
}
C vous permet également de définir des fonctions qui prennent des fonctions comme arguments, avec une belle syntaxe lisible qui ressemble à un appel de fonction (enfin, c'est lisible, tant que vous ne retournerez pas de pointeur sur la fonction).
#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);
}
Comme je l'ai mentionné, C permet d'omettre les noms d'argument dans les fichiers d'en-tête, donc output_result
cela ressemblerait à ceci dans le fichier d'en-tête.
int output_result(int());
Un argument dans le constructeur
Vous ne reconnaissez pas celui-là? Eh bien, laissez-moi vous le rappeler.
A a(B());
Oui, c'est exactement la même déclaration de fonction. A
est int
, a
est output_result
et B
est int
.
Vous pouvez facilement remarquer un conflit de C avec les nouvelles fonctionnalités de C ++. Pour être exact, les constructeurs étant le nom de classe et les parenthèses, et la syntaxe de déclaration alternative avec ()
au lieu de =
. De par sa conception, C ++ essaie d'être compatible avec le code C, et par conséquent, il doit gérer ce cas - même si pratiquement personne ne s'en soucie. Par conséquent, les anciennes fonctionnalités C ont priorité sur les nouvelles fonctionnalités C ++. La grammaire des déclarations essaie de faire correspondre le nom en tant que fonction, avant de revenir à la nouvelle syntaxe avec ()
si elle échoue.
Si l'une de ces fonctionnalités n'existait pas ou avait une syntaxe différente (comme {}
dans C ++ 11), ce problème ne se serait jamais produit pour la syntaxe avec un argument.
Maintenant, vous pouvez demander pourquoi A a((B()))
fonctionne. Eh bien, déclarons output_result
avec des parenthèses inutiles.
int output_result((int()));
Ça ne marchera pas. La grammaire exige que la variable ne soit pas entre parenthèses.
<stdin>:1:19: error: expected declaration specifiers or ‘...’ before ‘(’ token
Cependant, C ++ attend ici une expression standard. En C ++, vous pouvez écrire le code suivant.
int value = int();
Et le code suivant.
int value = ((((int()))));
C ++ s'attend à ce que l'expression entre parenthèses soit ... eh bien ... expression, contrairement au type que C attend. Les parenthèses ne signifient rien ici. Cependant, en insérant des parenthèses inutiles, la déclaration de fonction C n'est pas mise en correspondance et la nouvelle syntaxe peut être mise en correspondance correctement (qui attend simplement une expression, telle que 2 + 2
).
Plus d'arguments dans le constructeur
Un argument est certainement intéressant, mais qu'en est-il de deux? Ce n'est pas que les constructeurs peuvent avoir un seul argument. L'une des classes intégrées qui prend deux arguments eststd::string
std::string hundred_dots(100, '.');
Tout va bien (techniquement, l'analyse serait la plus vexante si elle était écrite comme std::string wat(int(), char())
, mais soyons honnêtes - qui écrirait cela? Mais supposons que ce code a un problème épineux. Vous supposeriez que vous devez mettre tout entre parenthèses.
std::string hundred_dots((100, '.'));
Pas tout à fait.
<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>::
^
Je ne sais pas pourquoi g ++ essaie de se convertir char
en const char *
. Dans tous les cas, le constructeur a été appelé avec une seule valeur de type char
. Il n'y a pas de surcharge qui a un argument de type char
, donc le compilateur est confus. Vous vous demandez peut-être pourquoi l'argument est de type char?
(100, '.')
Oui, ,
voici un opérateur virgule. L'opérateur virgule prend deux arguments et donne l'argument de droite. Ce n'est pas vraiment utile, mais c'est quelque chose que je connais pour mon explication.
Au lieu de cela, pour résoudre l'analyse la plus vexante, le code suivant est nécessaire.
std::string hundred_dots((100), ('.'));
Les arguments sont entre parenthèses, pas l'expression entière. En fait, une seule des expressions doit être entre parenthèses, car il suffit de rompre légèrement avec la grammaire C pour utiliser la fonctionnalité C ++. Les choses nous amènent au point de zéro argument.
Zéro argument dans le constructeur
Vous avez peut-être remarqué la eighty_four
fonction dans mon explication.
int eighty_four();
Oui, cela est également affecté par l'analyse la plus vexante. C'est une définition valide, et celle que vous avez probablement vue si vous avez créé des fichiers d'en-tête (et vous devriez). L'ajout de parenthèses ne résout pas le problème.
int eighty_four(());
Pourquoi est-ce si? Eh bien, ce ()
n'est pas une expression. En C ++, vous devez mettre une expression entre parenthèses. Vous ne pouvez pas écrire auto value = ()
en C ++, car ()
cela ne veut rien dire (et même si c'était le cas, comme un tuple vide (voir Python), ce serait un argument, pas zéro). En pratique, cela signifie que vous ne pouvez pas utiliser la syntaxe abrégée sans utiliser la syntaxe de C ++ 11 {}
, car il n'y a pas d'expressions à mettre entre parenthèses et la grammaire C pour les déclarations de fonction s'appliquera toujours.
(B())
est juste une expression C ++, rien de plus. Ce n'est pas une sorte d'exception. La seule différence que cela fait est qu'il n'y a aucun moyen de l'analyser en tant que type, et donc ce n'est pas le cas.