Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it legitimate in modern C++ to define a return variable in the function declaration?

I found a strange piece of C++ grammar on CodeSignal:

string r, longestDigitsPrefix(string s)
{
   for(auto const c : s)
   {
      if(isdigit(c))
        r += c;
      else
        break;
   }
   return r;
}

The first line is defining string r before the function declaration. Is this valid in modern C++?

The above code compiles and passes all tests in the CodeSignal console, but it produced a compiler error when I tried to compile locally (--std=c++14).

Is this is valid grammar in modern C++? If so, which standard revision does it comply with?

like image 982
Harry Avatar asked Jan 20 '19 14:01

Harry


People also ask

Is variable is the declaration and definition of a function?

A variable or a function can be declared any number of times but, it can be defined only once. The above points are summarized in the following table as follows: Declaration. Definition. A variable or a function can be declared any number of times.

What is the difference between declaration and definition of a variable in C?

What is the difference between declaration and definition in C#? Declaration means that variable is only declared and memory is allocated, but no value is set. However, definition means the variables has been initialized. The same works for variables, arrays, collections, etc.

Does C need a function declaration?

Function declaration is required when you define a function in one source file and you call that function in another file. In such case, you should declare the function at the top of the file calling the function.

Is it possible to declare and define a variable at the same time?

Once something is defined, that also counts as declaring it; so you can often both declare and define a function, class or variable at the same time.


2 Answers

Yeah, C++ grammar is weird. Basically, when it comes to declarations (and only declarations), we have this thing where:

T D1, D2, ... ,Dn;

means ([dcl.dcl]/3):

T D1;
T D2;
...
T Dn;

This will be familiar in the normal cases:

int a, b; // declares two ints

And probably in the cases you've been told to worry about:

int* a, b, *c; // a and c are pointers to int, b is just an int

But declarators can introduce other things too:

int *a, b[10], (*c)[10], d(int);

Here a is a pointer to int, b is an array of 10 ints, c is a pointer to an array of 10 ints, and d is a function taking an int returning an int.


However, this only applies to declarations. So this:

string r, longestDigitsPrefix(string s);

is a valid C++ declaration that declares r to be a string and longestDigitsPrefix to be a function taking a string and returning a string.

But this:

string r, longestDigitsPrefix(string s) { return s; }

is invalid C++. Function definitions have their own grammar and cannot appear as part of the init-declarator-list.

The definition of that function is also bad, since it's using a global variable to keep track of state. So even if it were valid, longestDigitsPrefix("12c") would return "12" the first time but "1212" the second time...

like image 132
Barry Avatar answered Oct 17 '22 23:10

Barry


By reading the ISO C++14 draft N4140 Annex A [gram], I'm pretty sure it's incorrect since I cant find a way to deduce the grammar from a translation unit from

translation-unit -> declaration-seq -> declaration -> block-declaration | function-definition | linkage-specification | ...

function-definition: attribute-specifier-seqopt decl-specifier-seqopt declarator virt-specifier-seqopt function-body

declarator: ptr-declarator noptr-declarator parameters-and-qualifiers trailing-return-type

But your line is more the comma operator but its grammar is:

expression: assignment-expression | expression , assignment-expression

assignment-expression: conditional-expression | logical-or-expression | assignment-operator | initializer-clause | throw-expression

And there is no way from assignment-expression to function-definition

Update: Thanks to Barry, another way to try to buttom-up parse your text is by rather try to get from a init-declarator-list (which you can get from block-declaration) to a function-definition:

init-declarator-list: init-declarator | init-declarator-list , init-declarator

init-declarator: declarator initializeropt

And

declarator: ptr-declarator noptr-declarator parameters-and-qualifiers trailing-return-type

Would allow you a function declaration but not definition. So this odd code would be legal:

#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;
}

However I could be wrong, since I'm not used to use the formal grammar of C++, which is normal since it's grammar is very complex, and has some non trivial behaviour.

like image 25
Superlokkus Avatar answered Oct 17 '22 21:10

Superlokkus