Я нашел странный фрагмент грамматики 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).
Это допустимая грамматика в современном С++? Если да, то какой версии стандарта он соответствует?
@MatthieuBrucher - При ближайшем рассмотрении можно увидеть разные отрасли производства грамматики.
@StoryTeller В том смысле, что это действительно или недействительно?
@MatthieuBrucher - недействительно. Производство грамматики, которое я искал, описывает decl a; decl b; - a или b могут быть определениями функций (обратите внимание на точку с запятой). Но определение функции может не отображаться в decl a, b;.
string r, longestDigitsPrefix(string s); будет действительным. Код, который вы разместили, недействителен.
@StoryTeller Спасибо за проверку!
Вы действительно должны опубликовать версию MCVE, которую вы сами использовали со своим компилятором.
Является ли этот фрагмент кода частью более крупного кода? Вы можете добавить больше контекста в этот фрагмент кода?





Насколько мне известно, это недопустимое объявление функции.
Префикс в функциях может использоваться только как способ определить, какой тип является "возвращаемой" переменной функции, а не самой переменной.
Однако вы можете объявить переменную вне вашей функции (таким образом сделав ее глобальной) и изменить ее внутри функции, как вы, возможно, пытались, но тогда вам нужно передать указанную переменную в качестве ссылки в аргументах вашей функции. Вам также нужно будет установить свою функцию как недействительную, так как она не будет возвращать переменную, а просто изменит ее.
Пример
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++.
Прочитав проект 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
Оператор «запятая» может использоваться как часть выражение, но предлагаемый здесь код синтаксически не является выражением (это утверждение), поэтому я не думаю, что нужно смотреть на оператор «запятая» при опровержении того, что это синтаксически правильный.
@templatetypedef Трудно рассуждать о грамматически неверных утверждениях, я пытался охватить все случаи, о которых можно было подумать, увидев запятую в таком контексте, но я понимаю вашу точку зрения.
@templatetypedef: это не заявление; они могут появляться только внутри блоков операторов.
@ Бен Фойгт, я исправлен! Каким же тогда будет этот синтаксис? Просто чистая декларация?
@templatetypedef: Да. Ну, юридический синтаксис (с разделением на две части). Это то, что позволяет ему появляться в области тела файла/пространства имен/класса, где утверждения незаконны.
Если вы делаете это как оптимизацию, то, пока возвращается только одна переменная, большинство компиляторов будут располагать возвращаемую переменную в стеке вызывающей стороны, а не в стеке вызываемой.
Все, что вам нужно сделать, это объявить переменную в контексте вызываемого объекта и убедиться, что существует либо 1 точка возврата, либо все точки возврата возвращают одну и ту же переменную.
Поскольку вопрос имеет тег language-lawyer, мы ожидаем ответов со стандартными кавычками C++.
Где вы это нашли и какой компилятор это принимает? Никогда не видел эту (недопустимую) грамматику.