Законно ли в современном C ++ определять возвращаемую переменную в объявлении функции? - PullRequest
0 голосов
/ 20 января 2019

Я нашел странный фрагмент грамматики C ++ на CodeSignal :

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

Первая строка определяет string r перед объявлением функции.Это допустимо в современном C ++?

Приведенный выше код компилируется и проходит все тесты в консоли CodeSignal, но выдает ошибку компилятора при попытке локальной компиляции (--std=c++14).

Это действительная грамматика в современном C ++?Если да, то какой стандартной редакции он соответствует?

Ответы [ 4 ]

0 голосов
/ 20 января 2019

Если вы делаете это как оптимизацию, то, пока возвращается только одна переменная, большинство компиляторов будут иметь возвращаемую переменную в стеке вызывающего, а не в стеке вызываемого.

Все, что вам нужно сделать, это объявить переменную в контексте вызываемого и убедиться, что существует либо 1 точка возврата, либо все точки возврата возвращают одну и ту же переменную.

0 голосов
/ 20 января 2019

Читая черновик ISO A ++ 14 N4140 Приложение A [грамм], я почти уверен, что это неверно, поскольку я не могу найти способ вывести грамматику из единицы перевода из

единица перевода -> объявление-последовательность -> объявление -> объявление блока |определение функции |спецификация связи |...

определение функции: атрибут-спецификатор-seqopt decl-спецификатор-seqopt декларатор virt-спецификатор-seqopt тело-функции

декларатор: ptr-декларатор noptr-декларатор параметры-и-квалификаторы trailing-return-type

Но ваша строка - это больше оператор запятой, а его грамматика:

выражение: присваивание-выражение |выражение, присваивание-выражение

присваивание-выражение: условное выражение |логическое или выражение |оператор присваивания |инициализатор-предложение |throw-expression

И нет пути от assignment-expression до function-definition

Обновление: благодаря Барри, еще один способ попытаться проанализировать ваш текст -попробуйте перейти от init-declarator-list (который вы можете получить от block-declaration) к function-definition:

init-Заявитель-список-списку: init-декларатор |список инициаторов объявления, декларатор инициалов

инициатор декларации: декларатор initializeropt

и

декларатор: ptr-декларатор noptr-декларатор параметры-and-qualifiers trailing-return-type

Позволит вам объявление функции, но не определение.Так что этот странный код был бы законным:

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

Однако я могу ошибаться, поскольку я не привык использовать формальную грамматику C ++, что является нормальным, поскольку ее грамматика очень сложна и имеет некоторыенетривиальное поведение.

0 голосов
/ 20 января 2019

Да, C ++ грамматика странная.По сути, когда дело доходит до объявлений (и только объявлений), у нас есть такая вещь, где:

T D1, D2, ... ,Dn;

означает ( [dcl.dcl] / 3 ):

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

Это будет знакомо в обычных случаях:

int a, b; // declares two ints

И, вероятно, в случаях, о которых вам было сказано беспокоиться:

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

Но деклараторы могут вводить и другие вещи:

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

Здесь a - указатель на int, b - массив из 10 int с, c - указатель на массив10 int с, и d - функция, принимающая int, возвращающую int.

Однако, это только относится к объявлениям.Итак, это:

string r, longestDigitsPrefix(string s);

является допустимым объявлением C ++, которое объявляет r как string и longestDigitsPrefix как функцию, принимающую string и возвращающую string.

Но это:

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

- недопустимый C ++.Определения функций имеют собственную грамматику и не могут отображаться как часть init-Declarator-list .

Определение этой функции также неверно, поскольку она использует глобальную переменную для отслеживания состояния.Таким образом, даже если бы оно было действительным, longestDigitsPrefix("12c") вернуло бы "12" в первый раз, но "1212" во второй раз ...

0 голосов
/ 20 января 2019

Насколько мне известно, это недопустимое объявление функции.

Префикс в функциях может использоваться только для определения типа, который является «возвращающей» переменной функции, а не самой переменной.

Однако вы можете объявить переменную вне вашей функции (таким образом, сделав ее глобальной) и изменить ее внутри функции, как вы, возможно, пытались это сделать, но затем вам нужно передать указанную переменную в качестве ссылки в аргументах вашей функции.Вам также необходимо установить вашу функцию как void, поскольку она не собирается возвращать переменную, а просто изменяет ее

Пример

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);
...