J'ai trouvé un étrange morceau de grammaire C++ sur CodeSignal :
string r, longestDigitsPrefix(string s)
{
for(auto const c : s)
{
if(isdigit(c))
r += c;
else
break;
}
return r;
}
La première ligne définit string r
avant la déclaration de fonction. Est-ce valable dans le C++ moderne?
Le code ci-dessus compile et passe tous les tests dans la console CodeSignal, mais il a produit une erreur de compilation lorsque j'ai essayé de compiler localement (--std=c++14
).
Est-ce une grammaire valide en C++ moderne? Si oui, à quelle révision standard est-elle conforme?
Oui, la grammaire C++ est bizarre. Fondamentalement, en ce qui concerne les déclarations (et uniquement les déclarations), nous avons cette chose où:
T D1, D2, ... ,Dn;
signifie ( [dcl.dcl]/ ):
T D1;
T D2;
...
T Dn;
Cela sera familier dans les cas normaux:
int a, b; // declares two ints
Et probablement dans les cas où l'on vous a dit de vous inquiéter:
int* a, b, *c; // a and c are pointers to int, b is just an int
Mais les déclarants peuvent également introduire d'autres choses:
int *a, b[10], (*c)[10], d(int);
Ici a
est un pointeur vers int, b
est un tableau de 10 int
s, c
est un pointeur vers un tableau de 10 int
s et d
est une fonction prenant un int
renvoyant un int
.
Cependant, ceci uniquement s'applique aux déclarations. Donc ça:
string r, longestDigitsPrefix(string s);
est une déclaration C++ valide qui déclare que r
est un string
et longestDigitsPrefix
une fonction prenant un string
et retournant un string
.
Mais ça:
string r, longestDigitsPrefix(string s) { return s; }
n'est pas C++ non valide. Les définitions de fonction ont leur propre grammaire et ne peuvent pas apparaître dans la liste init-declarator-list .
La définition de cette fonction est également mauvaise, car elle utilise une variable globale pour suivre l'état. Donc, même s'il était valide, longestDigitsPrefix("12c")
retournerait "12"
La première fois mais "1212"
La deuxième fois ...
En lisant le projet N4140 de l'annexe A de l'ISO C++ 14, je suis sûr qu'il est incorrect car je ne peux pas trouver un moyen de déduire la grammaire d'une unité de traduction de
translation-unit -> declaration-seq -> declaration -> block-declaration | définition-fonction | spécification de liaison | ...
définition de fonction: attribut-spécificateur-seqopt décl-spécificateur-seqopt déclarateur virt-spécificateur-seqopt corps-fonction
declarator: ptr-declarator noptr-declarator paramètres-et-qualificateurs trailing-return-type
Mais votre ligne est plus l'opérateur virgule mais sa grammaire est:
expression: expression-affectation | expression, expression-affectation
expression-affectation: expression-conditionnelle | expression-logique | opérateur d'affectation | clause-initialiseur | jet-expression
Et il n'y a aucun moyen de assignment-expression
à function-definition
Mise à jour: grâce à Barry, une autre façon d'essayer d'analyser votre texte est plutôt d'essayer d'obtenir d'un init-declarator-list
(que vous pouvez obtenir auprès de block-declaration
) à un function-definition
:
init-declarator-list: init-declarator | init-declarator-list, init-declarator
init-declarator: déclarator initializeropt
Et
declarator: ptr-declarator noptr-declarator paramètres-et-qualificateurs trailing-return-type
Vous permettrait une déclaration de fonction mais pas une définition. Donc, ce code étrange serait légal:
#include <string>
using std::string;
string r, longestDigitsPrefix(string s);
string longestDigitsPrefix(string s) {
for(auto const c : s)
{
if(isdigit(c))
r += c;
else
break;
}
return r;
}
int main(int argc, char *argv[]) {
longestDigitsPrefix("foo");
return 0;
}
Cependant, je peux me tromper, car je n'ai pas l'habitude d'utiliser la grammaire formelle de C++, ce qui est normal car sa grammaire est très complexe et a un comportement non trivial.