with - mingw c++ 17




Code étrange qui compile avec g++ (2)

Le code suivant compile avec succès avec g ++ 4.8.1:

int main()
{
    int(*)();
}

Cela ressemble à une simple déclaration d'un pointeur sur la fonction:

int(*f)();

Il ne compile pas avec clang 3.4 et vc ++ 2013.

Est-ce un bug de compilateur ou l'un des endroits sombres de la norme?

Liste de pièces de code étranges similaires qui se compilent bien avec g ++ 4.8.1 (mis à jour):

  1. int(*)();

  2. int(*);

  3. int(*){};

  4. int(*());

Vivez l'exemple avec ces étranges morceaux de code .

Mise à jour 1: @Ali ajouté quelques informations intéressantes dans les commentaires:

Les 4 cas donnent une erreur de compilation avec le tronc clang 3.5 (202594) et compilent correctement avec le tronc gcc 4.9 (20140302). Le comportement est le même avec -std=c++98 -pedantic , sauf pour int(*){}; ce qui est compréhensible; listes d'initialisation étendues uniquement disponibles avec -std=c++11 .

Mise à jour 2: Comme @CantChooseUsernames noté dans sa réponse, ils compilent encore bien même avec l'initialisation et aucun assemblage n'est généré pour eux par g ++ (ni avec ni sans initialisation) même sans aucune optimisation activée:

  1. int(*)() = 0;

  2. int(*) = 0;

  3. int(*){} = 0;

  4. int(*()) = 0;

Exemple en direct avec les initialisations .

Mise à jour 3: J'ai été vraiment surpris de constater que int(*)() = "Hello, world!"; compile bien, aussi (alors que int(*p)() = "Hello, world!"; ne compile pas, bien sûr).

Mise à jour 4: C'est fantastique mais int(*){} = Hello, world!; compile bien. Et le morceau de code extrêmement étrange suivant: int(*){}() = -+*/%&|^~.,:!?$()[]{}; ( exemple vivant ).

Mise à jour 5: Comme l' @zwol noté @zwol dans son commentaire

Ceci et un certain nombre de problèmes syntaxiques liés sont suivis comme bug 68265 gcc.


Je ne suis pas sûr combien cela aide, mais j'ai essayé ce qui suit (clang 3.3, g ++ 4.8.1):

using P = int(*)();
using Q = int*;
P; // warning only
Q; // warning only
int(*)(); // error (but only in clang)
int*;     // error
int(*p)(); // ok
int *q;    // ok

D'un autre côté, tout se compile bien en g ++ 4.8.2 et 4.9.0. Je n'ai pas de claquement 3.4, malheureusement.

Très grossièrement , une déclaration [iso section 7] comprend les parties suivantes dans l'ordre:

  1. spécificateurs de préfixe facultatifs (par exemple static , virtual )
  2. type de base (par exemple const double , vector<int> )
  3. déclarateur (par exemple n , *p , a[7] , f(int) )
  4. noexcept fonction de suffixe optionnels (par exemple, const , noexcept )
  5. initialiseur optionnel ou corps de la fonction (par exemple = {1,2,3} ou { return 0; }

Maintenant, un déclarateur se compose à peu près d'un nom et éventuellement de quelques opérateurs de déclarateurs [iso 8/4].

Opérateurs de préfixe, par exemple:

  • * (pointeur)
  • *const (pointeur constant)
  • & (référence lvalue)
  • && (référence rvalue)
  • auto (type de retour de fonction, à la fin)

Opérateurs de Postfix, par exemple:

  • [] (tableau)
  • () (fonction)
  • -> (type de retour de fonction)

Les opérateurs ci-dessus ont été conçus pour refléter leur utilisation dans les expressions. Les opérateurs de Postfix se lient plus que préfixe, et les parenthèses peuvent être utilisées pour changer leur ordre: int *f() est une fonction renvoyant un pointeur sur int , alors que int (*f)() est un pointeur vers une fonction retournant int .

Peut-être que je me trompe, mais je pense que ces opérateurs ne peuvent pas être dans la déclaration sans le nom. Donc, quand nous écrivons int *q; , alors int est le type de base, et *q est le déclarateur constitué de préfixe opérateur * suivi du nom q . Mais int *; ne peut pas apparaître seul.

D'un autre côté, lorsque nous définissons en using Q = int*; , puis déclaration Q; est bien en soi parce que Q est le type de base. Bien sûr, parce que nous ne déclarons rien, nous pouvons avoir une erreur ou un avertissement en fonction des options du compilateur, mais c'est une erreur différente.

Ce qui précède est juste ma compréhension. Ce que dit la norme (par exemple N3337) est [iso 8.3 / 1]:

Chaque déclarateur contient exactement un identificateur de déclarateur ; il nomme l'identifiant qui est déclaré. Un identificateur non qualifié figurant dans un identificateur-déclarant doit être un identificateur simple à l'exception de la déclaration de certaines fonctions spéciales (12.3 [ conversions définies par l'utilisateur ], 12.4 [destructeurs], 13.5 [opérateurs surchargés]) et de la déclaration des spécialisations ou spécialisations partielles (14.7).

(Les notes entre crochets sont à moi). Donc, je comprends int(*)(); devrait être invalide et je ne peux pas dire pourquoi il a un comportement différent en clang et différentes versions de g ++.


Selon la norme C ++ (page 6 de la section 7 Déclarations)

6 Chaque init-déclarator dans la liste init-declarator-list contient exactement un identificateur de déclarateur , qui est le nom déclaré par ce déclarateur d'initialisation et donc l'un des noms déclarés par la déclaration

Donc, c'est simplement un bug de compilateur.

Le code valide pourrait ressembler par exemple (à part la déclaration de pointeur de fonction montrée par vous) bien que je ne puisse pas le compiler avec mon MS VC ++ 2010.

int(*p){};

Il semble que le compilateur que vous utilisez pour tester permette des déclarations sans identificateur de déclarant.

Tenez également compte du paragraphe suivant de la section 8.1 Noms de types

1 Pour spécifier explicitement les conversions de type et en tant qu'argument de sizeof, alignof, new ou typeid , le nom d'un type doit être spécifié. Cela peut être fait avec un identifiant de type, qui est syntaxiquement une déclaration pour une variable ou une fonction de ce type qui omet le nom de l'entité.







g++