167 votes

Analyse la plus vexante: pourquoi ne pas a (()); travail?

Parmi les nombreuses choses Débordement de Pile m'a appris est ce qui est connu comme "la plus délicate à analyser", qui est classiquement démontré avec une ligne comme

A a(B()); //declares a function

Tout cela, pour la plupart, intuitivement semble être la déclaration d'un objet a de type A, en prenant temporaire B objet en tant que paramètre du constructeur, c'est en fait une déclaration d'une fonction a retourner un A, en prenant un pointeur vers une fonction qui retourne B et elle ne prend pas de paramètres. De même, la ligne de

A a(); //declares a function

relève aussi de la même catégorie, puisqu'au lieu d'un objet, il déclare une fonction. Maintenant, dans le premier cas, la solution de contournement pour ce problème consiste à ajouter un supplément de jeu de crochets/parenthèses autour de l' B(), comme le compilateur sera alors l'interpréter comme la déclaration d'un objet

A a((B())); //declares an object

Cependant, dans le second cas, en faisant de même conduit à une erreur de compilation

A a(()); //compile error

Ma question est, pourquoi? Oui, je suis très bien conscient que la bonne "solution de contournement" est à changer en A a;, mais je suis curieux de savoir ce que c'est que les extra - () pour le compilateur dans le premier exemple qui ensuite ne fonctionne pas lorsque le réappliquer dans le deuxième exemple. Est l' A a((B())); solution de contournement d'une exception spécifique écrit dans la norme? Merci.

73voto

Brian R. Bondy Points 141769

Il n'est pas éclairé réponse, c'est juste parce que ce n'est pas définie comme une syntaxe valide par le langage C++... il est donc, par définition de la langue.

Si vous avez une expression à l'intérieur, il est valable. Par exemple:

 ((0));//compiles

Pour en savoir plus sur la façon dont les langues sont définies, et comment les compilateurs de travail, vous devriez en apprendre davantage sur la théorie des langages Formels ou plus précisément des Grammaires hors Contexte (CFG) et de matériel connexe, comme des machines à états finis. Si vous êtes intéressé par ce bien que les pages de wikipédia ne sera pas suffisant, vous aurez à obtenir un livre.

41voto

David Points 1773

La solution finale à ce problème consiste à passer à la syntaxe d'initialisation uniforme C + 11 si vous le pouvez.

 A a{};
 

http://www.stroustrup.com/C++11FAQ.html#uniform-init

31voto

xfix Points 2890

Fonction C declarators

Tout d'abord, il y a C. En C, A a() est de déclaration de fonction. Par exemple, putchar a la déclaration suivante. Normalement, de telles déclarations sont stockées dans des fichiers d'en-tête, mais rien ne vous empêche de les écrire manuellement, si vous savez comment la déclaration de la fonction ressemble. Les noms d'arguments sont facultatifs dans les déclarations, j'ai donc omis dans cet exemple.

int putchar(int);

Cela permet d'écrire le code comme ceci.

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 de belles lisible syntaxe qui ressemble à un appel de fonction (eh bien, c'est lisible, tant que vous n'aurez pas de retourner un pointeur de 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 l' output_result ressemblerait à ceci dans le fichier d'en-tête.

int output_result(int());

Un argument au constructeur

Ne reconnaissez-vous pas? Eh bien, permettez-moi de vous rappeler.

A a(B());

Yep, 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 de nouvelles fonctionnalités de C++. Pour être exact, les constructeurs étant nom de la classe et de la parenthèse, et suppléant de la syntaxe de déclaration avec () au lieu de =. De par sa conception, C++ essaie d'être compatible avec le code en C, et donc il doit composer avec cette affaire - même si pratiquement personne ne s'en soucie. Donc, vieux C caractéristiques ont la priorité sur les nouvelles fonctionnalités C++. La grammaire de déclarations essaie de faire correspondre le nom de la fonction, avant de revenir à la nouvelle syntaxe, () si elle échoue.

Si l'une de ces fonctions n'existent pas, ou a une syntaxe différente (comme {} en C++11), ce problème ne serait jamais arrivé pour la syntaxe avec un argument.

Maintenant, vous demandez peut-être pourquoi A a((B())) travaux. Eh bien, nous allons déclarer output_result inutile parenthèses.

int output_result((int()));

Il ne fonctionnera pas. La grammaire exige la variable de ne pas être entre parenthèses.

<stdin>:1:19: error: expected declaration specifiers or ‘...' before ‘(' token

Cependant, C++ s'attend à ce standard d'expression ici. En C++, vous pouvez écrire le code suivant.

int value = int();

Et le code suivant.

int value = ((((int()))));

C++ attend expression à l'intérieur à l'intérieur des parenthèses pour être... eh bien... d'expression, contrairement à la type C attend. Les parenthèses ne veux pas dire quoi que ce soit ici. Toutefois, en insérant inutile parenthèses, la fonction C de la déclaration n'est pas adapté, et la nouvelle syntaxe peut être appariés correctement (ce qui attend simplement une expression, telle qu' 2 + 2).

En plus des arguments dans le constructeur

Sûrement un argument, c'est sympa, mais ce que sur les deux? Ce n'est pas que les constructeurs peuvent avoir un seul argument. L'une des classes intégrées qui prend deux arguments est - std::string

std::string hundred_dots(100, '.');

C'est super bien (techniquement, il aurait plus délicate à analyser si il serait écrit comme std::string wat(int(), char()), mais soyons honnêtes - qui serait d'écrire? Mais supposons que ce code a un problème épineux. Vous pourriez penser que vous devez mettre tout entre parenthèses.

std::string hundred_dots((100, '.'));

Pas tout à fait ainsi.

<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 convertir char de const char *. De toute façon, le constructeur a été appelé avec juste une valeur de type char. Il n'y a pas de surcharge qui a un argument de type char, donc le compilateur est confus. Vous pouvez demander - pourquoi l'argument est de type char?

(100, '.')

Oui, , ici est un opérateur virgule. L'opérateur virgule prend deux arguments, et donne le côté droit de l'argument. Il n'est pas vraiment utile, mais c'est quelque chose d'être connu pour mes explications.

Au lieu de cela, pour résoudre la plus délicate à analyser, le code suivant est nécessaire.

std::string hundred_dots((100), ('.'));

Les arguments sont dans les parenthèses, pas la totalité de l'expression. En fait, l'une des expressions de besoins pour être entre parenthèses, comme c'est suffisant pour casser le C de la grammaire légèrement à utiliser le C++ fonctionnalité. Les choses qui nous amène au point de zéro arguments.

Les arguments zéro dans le constructeur

Vous avez peut-être remarqué l' eighty_four fonction dans mon explication.

int eighty_four();

Oui, c'est aussi affectée par la plus délicate à analyser. C'est une définition valable, et on vous probablement avez vu 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 donc? Eh bien, () 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 () ne veut rien dire (et même si vous n'avez, comme vide de n-uplet (voir Python), il serait un argument, pas zéro). Pratiquement, cela signifie que vous ne pouvez pas utiliser d'abréviation syntaxe sans l'aide de C++11 {} de la syntaxe, comme il n'y a pas d'expressions pour mettre dans les parenthèses, et C de la grammaire pour les déclarations de fonction s'appliquent toujours.

12voto

user265149 Points 114

Vous pourriez au lieu de cela

 A a(());
 

utilisation

 A a=A();
 

6voto

Michael Burr Points 181287

Le plus intime des parenthèses dans votre exemple serait l'expression, et en C++, la grammaire définit un expression un assignment-expression ou d'une autre, expression suivi d'une virgule et un autre assignment-expression (Annexe A. 4 - résumé de la Grammaire et des Expressions).

La grammaire définit en outre un assignment-expression parmi plusieurs autres types d'expression, aucune ne peut être rien (ou seulement des espaces).

Donc, la raison pour laquelle vous ne pouvez pas avoir d' A a(()) est tout simplement parce que la grammaire ne le permet pas. Cependant, je ne peux pas répondre à pourquoi les gens qui ont créé le C++ ne permet pas cette utilisation particulière de vide parens, comme une sorte de cas - je suppose qu'ils préfèrent ne pas le mettre dans un tel cas, si il y avait une solution de rechange raisonnable.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X