简体   繁体   中英

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?

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 int s, c is a pointer to an array of 10 int s, 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...

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.

To my knowledge, this is not a valid function declaration.

The prefix in functions can be used only as a way to define what type is the function's "returning" variable, not the variable itself.

You can however declare a variable outside your function (thus making it global) and modify it inside the function, as you may have probably attempted, but then you need to pass said variable as a reference in your function's arguments. You would also need to set your function as void, since it is not going to return the variable, but just modify it

Example

std::string foo = "Hello";

void foo_func(std::string &string)
{ //do something with the string
}

//call the function and modify the global "foo" string
foo_func(foo);

If you are doing this as an optimization, then as long as there is only one variable being returned, then most compilers will have the return variable allocated on the caller's stack, rather than the callee's.

All you have to do is declare the variable in callee's context, and make sure there is either 1 return point, or all return points return the same variable.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM