int a [] = {1,2,};Странная запятая разрешена.Любая конкретная причина? - PullRequest
323 голосов
/ 12 августа 2011

Возможно, я не с этой планеты, но мне кажется, что синтаксическая ошибка должна быть следующей:

int a[] = {1,2,}; //extra comma in the end

Но это не так. Я был удивлен, когда этот код скомпилирован в Visual Studio, но я научился не доверять компилятору MSVC в том, что касается правил C ++, поэтому я проверил стандарт и он равен , разрешенным стандарт также. Вы можете увидеть 8.5.1 правила грамматики, если не верите мне.

enter image description here

Почему это разрешено? Это может быть глупый бесполезный вопрос, но я хочу, чтобы вы поняли, почему я спрашиваю. Если бы это был подслучайный случай общего правила грамматики, я бы понял - они решили не усложнять общую грамматику, просто запретив избыточную запятую в конце списка инициализатора. Но нет, дополнительная запятая явно разрешено. Например, нельзя использовать избыточную запятую в конце списка аргументов вызова функции (когда функция принимает ...), , что является нормальным .

Итак, опять же, есть ли какая-то особая причина, по которой эта избыточная запятая явно разрешена?

Ответы [ 19 ]

425 голосов
/ 12 августа 2011

Это облегчает генерацию исходного кода, а также написание кода, который может быть легко расширен позже. Подумайте, что требуется для добавления дополнительной записи:

int a[] = {
   1,
   2,
   3
};

... вы должны добавить запятую к существующей строке и добавить новую строку. Сравните это со случаем, когда три уже имеют запятую после него, где вам просто нужно добавить строку. Аналогично, если вы хотите удалить строку, вы можете сделать это, не беспокоясь о том, является ли она последней или нет, и вы можете изменить порядок строк, не запутываясь в запятых. По сути, это означает, что в отношении к линиям есть единообразие.

Теперь подумайте о генерации кода. Нечто подобное (псевдокод):

output("int a[] = {");
for (int i = 0; i < items.length; i++) {
    output("%s, ", items[i]);
}
output("};");

Не нужно беспокоиться о том, является ли текущий элемент, который вы пишете, первым или последним. Гораздо проще.

125 голосов
/ 12 августа 2011

Это полезно, если вы делаете что-то вроде этого:

int a[] = {
  1,
  2,
  3, //You can delete this line and it's still valid
};
38 голосов
/ 12 августа 2011

Простота использования для разработчика, я бы подумал.

int a[] = {
            1,
            2,
            2,
            2,
            2,
            2, /*line I could comment out easily without having to remove the previous comma*/
          }

Кроме того, если по какой-либо причине у вас есть инструмент, который генерирует код для вас;инструмент не должен заботиться о том, является ли он последним элементом в инициализации или нет.

32 голосов
/ 12 августа 2011

Я всегда предполагал, что это облегчает добавление лишних элементов:

int a[] = {
            5,
            6,
          };

просто становится:

int a[] = { 
            5,
            6,
            7,
          };

позднее.

21 голосов
/ 16 августа 2011

Все, что все говорят о легкости добавления / удаления / генерации строк, является правильным, но реальный смысл этого синтаксиса - объединение исходных файлов.Представьте, что у вас есть этот массив:

int ints[] = {
    3,
    9
};

И предположим, что вы проверили этот код в хранилище.

Затем ваш друг отредактирует его, добавив в конец:

int ints[] = {
    3,
    9,
    12
};

И вы одновременно редактируете его, добавляя в начало:

int ints[] = {
    1,
    3,
    9
};

Семантически эти виды операций (добавление в начало, добавление в конец) должны быть полностью безопасны для слияния, и ваше ПО для управления версиями(надеюсь, Git) должен быть в состоянии automerge.К сожалению, это не так, потому что у вашей версии нет запятой после 9, а у вашего приятеля - нет.Принимая во внимание, что если бы в оригинальной версии был конечный 9, они бы автоматически объединялись.

Итак, мое практическое правило таково: используйте конечную запятую, если список занимает несколько строк, не используйте ее, если списокна одной строке.

15 голосов
/ 12 августа 2011

Завершающая запятая, я считаю, разрешена по причинам обратной совместимости. Существует много существующего кода, в основном сгенерированного автоматически, который ставит запятую. Это облегчает написание цикла без особых условий в конце. например,

for_each(my_inits.begin(), my_inits.end(),
[](const std::string& value) { std::cout << value << ",\n"; });

На самом деле для программиста нет никаких преимуществ.

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

12 голосов
/ 12 августа 2011

Насколько мне известно, одна из причин, по которой это разрешено, заключается в простоте автоматической генерации кода;вам не нужно никакой специальной обработки для последнего элемента.

11 голосов
/ 12 августа 2011

Это облегчает генераторы кода, которые выплевывают массивы или перечисления.

Представьте себе:

std::cout << "enum Items {\n";
for(Items::iterator i(items.begin()), j(items.end); i != j; ++i)
    std::cout << *i << ",\n";
std::cout << "};\n";

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

Если генератор кода написан, например, на Python, легко избежать разбрызгивания завершающей запятой с помощью функции str.join():

print("enum Items {")
print(",\n".join(items))
print("}")
8 голосов
/ 19 октября 2015

Я удивлен, после того, как все это время никто не цитировал Аннотированное C ++ Справочное руководство ( ARM ), в нем говорится следующее о [dcl.init] с акцентом на мое:

Очевидно, что слишком много обозначений для инициализации, но, похоже, каждая из них хорошо подходит для определенного стиля использования. Нотация = {initializer_list, opt} была унаследована от C и хорошо подходит для инициализации структур данных и массивов.[...]

, хотя грамматика изменилась с тех пор, как было написано ARM , происхождение остается.

, и мы можем перейти к обоснованию C99 чтобы понять, почему это было разрешено в C, и там написано:

K & R разрешает использование запятой в инициализаторе в конце списка инициализаторов.Стандарт сохранил этот синтаксис, поскольку он обеспечивает гибкость при добавлении или удалении элементов из списка инициализатора и упрощает машинное создание таких списков.

8 голосов
/ 20 апреля 2018

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

int a [] = {
#ifdef A
    1, //this can be last if B and C is undefined
#endif
#ifdef B
    2,
#endif
#ifdef C
    3,
#endif
};

Добавление макросов для обработки последних , было бы большой болью. С этим небольшим изменением синтаксиса этим тривиально управлять. И это более важно, чем машинно-генерируемый код, потому что, как правило, гораздо проще сделать это в полном языке Тьюринга, чем очень ограниченный препроцессор.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...