web-dev-qa-db-fra.com

Est-il légitime en C ++ moderne de définir une variable de retour dans la déclaration de fonction?

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?

40
Harry

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 ints, c est un pointeur vers un tableau de 10 ints 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 ...

54
Barry

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.

4
Superlokkus