Законно ли в современном С++ определять возвращаемую переменную в объявлении функции?

Я нашел странный фрагмент грамматики C++ на КодСигнал:

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

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

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

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

Где вы это нашли и какой компилятор это принимает? Никогда не видел эту (недопустимую) грамматику.

Matthieu Brucher 20.01.2019 15:23

@MatthieuBrucher - При ближайшем рассмотрении можно увидеть разные отрасли производства грамматики.

StoryTeller - Unslander Monica 20.01.2019 15:34

@StoryTeller В том смысле, что это действительно или недействительно?

Matthieu Brucher 20.01.2019 15:36

@MatthieuBrucher - недействительно. Производство грамматики, которое я искал, описывает decl a; decl b; - a или b могут быть определениями функций (обратите внимание на точку с запятой). Но определение функции может не отображаться в decl a, b;.

StoryTeller - Unslander Monica 20.01.2019 16:16
string r, longestDigitsPrefix(string s); будет действительным. Код, который вы разместили, недействителен.
KamilCuk 20.01.2019 16:17

@StoryTeller Спасибо за проверку!

Matthieu Brucher 20.01.2019 16:22

Вы действительно должны опубликовать версию MCVE, которую вы сами использовали со своим компилятором.

hyde 21.01.2019 12:56

Является ли этот фрагмент кода частью более крупного кода? Вы можете добавить больше контекста в этот фрагмент кода?

Luis Colorado 24.01.2019 05:07
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
42
8
3 563
4

Ответы 4

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

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

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

Пример

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

Поскольку вопрос имеет тег language-lawyer, мы ожидаем ответов со стандартными кавычками C++.

Matthieu Brucher 20.01.2019 18:01

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

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

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

expression: assignment-expression | expression , assignment-expression

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

И нет пути с assignment-expression на function-definition

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

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

init-declarator: declarator initializeropt

И

declarator: ptr-declarator noptr-declarator parameters-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++, что нормально, поскольку ее грамматика очень сложна и имеет нетривиальное поведение.

Да, грамматика 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; }

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

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

Я думаю, что ваш ответ неполный, можно было бы также подумать о «Встроенный оператор запятой. Выражения оператора запятой имеют вид E1, E2 В выражении запятой E1, E2 вычисляется выражение E1, его результат отбрасывается» en.cppreference.com/w/cpp/language/operator_other

Superlokkus 20.01.2019 17:03

Оператор «запятая» может использоваться как часть выражение, но предлагаемый здесь код синтаксически не является выражением (это утверждение), поэтому я не думаю, что нужно смотреть на оператор «запятая» при опровержении того, что это синтаксически правильный.

templatetypedef 20.01.2019 17:14

@templatetypedef Трудно рассуждать о грамматически неверных утверждениях, я пытался охватить все случаи, о которых можно было подумать, увидев запятую в таком контексте, но я понимаю вашу точку зрения.

Superlokkus 20.01.2019 18:03

@templatetypedef: это не заявление; они могут появляться только внутри блоков операторов.

Ben Voigt 21.01.2019 07:33

@ Бен Фойгт, я исправлен! Каким же тогда будет этот синтаксис? Просто чистая декларация?

templatetypedef 21.01.2019 07:53

@templatetypedef: Да. Ну, юридический синтаксис (с разделением на две части). Это то, что позволяет ему появляться в области тела файла/пространства имен/класса, где утверждения незаконны.

Ben Voigt 21.01.2019 07:54

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

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

Поскольку вопрос имеет тег language-lawyer, мы ожидаем ответов со стандартными кавычками C++.

Matthieu Brucher 20.01.2019 22:05

Другие вопросы по теме