Почему список инициализаторов с зависающей запятой разрешен в C ++ 11? [Дубликат]

Проверьте все пакеты, которые вы установили для php, используя:

yum list installed | grep remi

Установите все соответствующие пакеты php, особенно php-devel на вашем компьютере.

309
задан Columbo 1 December 2014 в 01:56
поделиться

19 ответов

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

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

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

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

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

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

409
ответ дан Peter Alexander 20 August 2018 в 23:22
поделиться
  • 1
    Кроме того, при использовании VCS "diff" между двумя версиями является более чистым, так как только одна строка изменяется, когда элемент добавляется или удаляется. – Kevin Panko 12 August 2011 в 22:05
  • 2
    @ Нестор: Почему «неудачный»? В чем тут недостаток? Просто потому, что некоторое внимание уделялось генерации кода (и легкой манипуляции) для одной крошечной части языка не означает, что это должно быть основной мотивацией всех решений на языке. Вывод типа, удаление полуколоней и т. Д. Имеют огромные последствия для языка. Здесь вы создаете ложную дихотомию, ИМО. – Jon Skeet 13 August 2011 в 06:59
  • 3
    @ Néstor: Вот где прагматизм побеждает над догматизмом: почему это должно быть полностью одно или полностью другое, когда больше полезно для быть смесью обоих? Как это на самом деле мешает, имея возможность добавить запятую в конце? Это непоследовательность, которая когда-либо мешала вам в любом смысле? Если нет, пожалуйста, взвесьте эту неуместную неэффективность против практических преимуществ , позволяющих запятую в конце. – Jon Skeet 13 August 2011 в 23:47
  • 4
    @Mrchief: Это не вопрос скорости ввода - это вопрос простоты, когда вы копируете, удаляете или переупорядочиваете предметы. Это сделало мою жизнь проще всего вчера. Без недостатков, почему not облегчает жизнь? Что касается попытки указать пальцем на MS, я сильно подозреваю, что это было на C до того, как Microsoft даже существовала ... Вы говорите, что это оправдание кажется странным, но я уверен, что он выигрывает тысячи разработчиков в сотнях компаний каждый день. Разве это не лучшее объяснение, чем поиск чего-то, что приносит пользу авторам-компиляторам? – Jon Skeet 17 August 2011 в 06:17
  • 5
    Это было в K & amp; R C. – Ferruccio 19 December 2011 в 22:08

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

int ints[] = {
    3,
    9
};

И предположим, что вы проверили этот код в репозитории.

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

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

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

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

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

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

15
ответ дан amoss 20 August 2018 в 23:22
поделиться

Единственный язык, на котором он - на практике * - не разрешен, - это Javascript, и это вызывает бесчисленное количество проблем. Например, если вы скопируете & amp; вставьте строку из середины массива, вставьте ее в конец и забудьте удалить запятую, тогда ваш сайт будет полностью разбит для ваших посетителей IE.

* Теоретически это разрешено, но Internet Explorer не соответствует стандарту и рассматривает его как ошибку

7
ответ дан Andreas Bonini 20 August 2018 в 23:22
поделиться
  • 1
    JavaScript & quot; массивы & quot; (которые являются только объектами с магическим свойством длины) довольно необычны: var x = [,,,] является законным (за исключением IE & lt; 9, но спецификация говорит, что это законно) – Peter C 12 August 2011 в 22:12
  • 2
    Согласно спецификации ECMAScript, это совершенно верно; в теории он должен работать в любом браузере, который реализует JavaScript в соответствии с указанной спецификацией, особенно часть спецификации, найденной здесь . – Dereleased 12 August 2011 в 22:59
  • 3
    К сожалению, JavaScript - это создание приложений для публики. Таким образом, нет, это не совсем правильно, когда у ~ 50% пользователей будут проблемы с использованием вашего приложения. И да, если бы я мог запретить IE & lt; 9 - слишком много часов тратит на то, что делает хороший код , работающий там ... – kgadek 12 August 2011 в 23:44
  • 4
    @ Dere: да, я так сказал в своем ответе =) – Andreas Bonini 12 August 2011 в 23:48
  • 5
    @Dereleased microsoft изобретает свои собственные спецификации и команды, которые другие соблюдают по крайней мере, что менталитет меняется (слава богу) – Chris McGrath 14 August 2011 в 03:14

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

12
ответ дан Fredrik Pihl 20 August 2018 в 23:22
поделиться

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

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

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

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

15
ответ дан Gene Bushuyev 20 August 2018 в 23:22
поделиться
  • 1
    Я не согласен полностью; [По моему мнению,] он нашел свой путь на многие языки, созданные задолго до C, потому что для программиста выгодно перемещаться по содержимому массива, прокомментировать строки волей-неволей и так далее, без необходимости беспокоиться о глупых синтаксических ошибках, вызванных транспозицией. Разве мы недостаточно напряжены? – Dereleased 12 August 2011 в 22:54
  • 2
    @Dereleased - по той же логике, почему нельзя допускать трейлинг (что-либо), как насчет int a = b + c +; или if(a && b &&); будет проще просто копировать и вставлять что-нибудь в конце и проще писать генераторы кода , Эта проблема является тривиальной и субъективной, и в таких случаях всегда полезно делать то, что лучше всего подходит для чтения кода. – Gene Bushuyev 12 August 2011 в 23:06
  • 3
    @Gene Bushuyev: Именно! У меня часто есть длинные выражения с + или & amp ;, с оператором в конце строки и, конечно же, мне нужно потратить некоторое дополнительное время, когда я хочу удалить последний операнд выражения. Я думаю, что этот синтаксис запятой действительно странный! – Giorgio 18 August 2011 в 07:15
  • 4
    @GeneBushuyev - Я не согласен с этими. Хотя предоставление конечных запятых в массивах и т. П. Является функцией удаления ошибок и облегчает вашу жизнь как программиста, я бы ради чистой читаемости принимал меры для удаления завершающих операторов AND (& amp;), плюсов и других разностей операторов из условных операторов. Это просто уродливо, ИМО. – Sune Rasmussen 22 November 2011 в 11:25
  • 5
    Что касается оператора &&, иногда я выполняю такие условия, как if (true \n && b1 \n && b2), чтобы я мог добавлять и удалять строки, как мне нужно. – Christian Mann 4 June 2016 в 14:12

В дополнение к генерации кода и редактированию, если вы хотите реализовать парсер, этот тип грамматики проще и проще реализовать. C # следует за этим правилом в нескольких местах, где есть список разделенных запятыми элементов, таких как элементы в определении enum.

2
ответ дан Iravanchi 20 August 2018 в 23:22
поделиться

Это позволяет защитить от ошибок, вызванных перемещением элементов в длинном списке.

Например, предположим, что у нас есть код, похожий на этот.

#include <iostream>
#include <string>
#include <cstddef>
#define ARRAY_SIZE(array) (sizeof(array) / sizeof *(array))
int main() {
    std::string messages[] = {
        "Stack Overflow",
        "Super User",
        "Server Fault"
    };
    size_t i;
    for (i = 0; i < ARRAY_SIZE(messages); i++) {
        std::cout << messages[i] << std::endl;
    }
}

И это здорово, поскольку это показывает оригинальную трилогию сайтов Stack Exchange.

Stack Overflow
Super User
Server Fault

Но есть одна проблема с этим. Как видите, нижний колонтитул на этом веб-сайте показывает «Ошибка сервера перед суперпользователем». Лучше исправить это, прежде чем кто-нибудь заметит.

#include <iostream>
#include <string>
#include <cstddef>
#define ARRAY_SIZE(array) (sizeof(array) / sizeof *(array))
int main() {
    std::string messages[] = {
        "Stack Overflow",
        "Server Fault"
        "Super User",
    };
    size_t i;
    for (i = 0; i < ARRAY_SIZE(messages); i++) {
        std::cout << messages[i] << std::endl;
    }
}

В конце концов, движущиеся линии вокруг не могут быть такими трудными, не так ли?

Stack Overflow
Server FaultSuper User

Я знаю, нет сайт под названием «Server FaultSuper User», но наш компилятор утверждает, что он существует. Теперь проблема заключается в том, что C имеет функцию конкатенации строк, которая позволяет вам писать две строки с двойными кавычками и конкатенировать их, используя ничего (аналогичная проблема также может возникать с целыми числами, поскольку знак - имеет несколько значений).

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

5
ответ дан Konrad Borowski 20 August 2018 в 23:22
поделиться

Это проще для машин, т. е. разбора и генерации кода.

Предполагая, что C, вы могли бы написать следующее?

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    puts("Line 1");
    puts("Line 2");
    puts("Line 3");

    return EXIT_SUCCESS
}

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

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

6
ответ дан Louis 20 August 2018 в 23:22
поделиться
  • 1
    Существуют языки (например, Pascal), которые позволяют это. То есть вы должны выбирать между ними; в качестве терминатора (C) или в качестве разделителя (Pascal). То же самое для ','. Было бы хорошо для меня, если ',' является терминатором, но тогда {1, 2, 3} должно быть синтаксической ошибкой. – Giorgio 18 August 2011 в 07:19

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

5
ответ дан Mark B 20 August 2018 в 23:22
поделиться

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

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

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("}")
11
ответ дан Maxim Egorushkin 20 August 2018 в 23:22
поделиться

Как и многие другие, конечная запятая в инициализаторе массива является одной из вещей, которые C ++ унаследовал от C (и им придется поддерживать навсегда). В книге «Deep C secrets» упоминается вид, совершенно отличный от тех, что указаны здесь.

В этом случае после примера с более чем одним «парамодическим знаком»:

char *available_resources[] = {
"color monitor"           ,
"big disk"                ,
"Cray"                      /* whoa! no comma! */
"on-line drawing routines",
"mouse"                   ,
"keyboard"                ,
"power cables"            , /* and what's this extra comma? */
};

мы читаем:

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

... для меня это имеет смысл

4
ответ дан Nikos Athanasiou 20 August 2018 в 23:22
поделиться
  • 1
    Запрет на запятую в деле enum несколько интересен, так как это тот случай, когда недостающая запятая представляла наименьшую двусмысленность. Учитывая struct foo arr[] = {{1,2,3,4,5}, {3,4,5,6,7}, }; существует два разумных значения, которые может присвоить язык: создать двухэлементный массив или создать трехэлементный массив, в котором последний элемент имеет значения по умолчанию. Если бы C принял более позднюю интерпретацию, я мог видеть, что запрещать enum foo {moe, larry, curly, }; по принципу, что должен быть только один способ написать заявление (без запятой), но ... – supercat 1 August 2015 в 19:40
  • 2
    ... учитывая, что C готов игнорировать запятую в случае, когда он мог бы разумно быть (но не был) назначен значительным значением (что было бы веским аргументом в пользу запрещения его там), любопытно, что это не «Желание в случае, когда запятая не может иметь значения [даже если один интерпретировал enum foo {moe,,larry,curly,}; как пропуск числа между moe и larry, обычно не имеет значения, обрабатывалась ли конечная запятая или игнорировалась , Единственный случай, когда это могло бы иметь значение, было бы, если последний элемент был максимальным значением для его объявленного типа, и что ... – supercat 1 August 2015 в 19:44
  • 3
    ... можно было бы просто сказать, что переполнение, которое происходит после последнего присвоенного значения перечисления, должно игнорироваться. – supercat 1 August 2015 в 19:44
  • 4
    @supercat Существуют языки, такие как C #, где априорные проектные исследования касаются возможностей IDE и интеграции при деволюции языка. C не был (и не мог быть) одним из этих языков. – Nikos Athanasiou 5 August 2015 в 12:43
  • 5
    Даже с такими языками, как C #, изменение целей дизайна привело к некоторым довольно серьезным несоответствиям в дизайне. Например, язык воздерживается от поддержки любой формы обратной перегрузки для обычных методов и операторов (даже если базовая инфраструктура может ее поддерживать), поскольку она рассматривалась как противоречащая цели простого языка компиляции, но лямбда-оценка включает в себя правила вывода типа, разрешение которых NP-завершено. Добавление новых правил перегрузки метода / оператора может привести к нарушению существующего кода (хотя я считаю, что хорошие правила могут свести к минимуму такую ​​опасность) ... – supercat 5 August 2015 в 14:57

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

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

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

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

в более позднюю дату.

32
ответ дан Oliver Charlesworth 20 August 2018 в 23:22
поделиться
  • 1
    Я не думаю, что сделать редактирование немного быстрее - это хорошая причина испортить синтаксис. IMHO это просто еще одна странная функция C ++. – Giorgio 12 August 2011 в 21:59
  • 2
    @Giorgio: Ну, это унаследовано от C. Вполне возможно, что это просто недосмотр в оригинальной спецификации языка, которая имеет полезный побочный эффект. – Oliver Charlesworth 12 August 2011 в 22:01
  • 3
    Хорошо, я не знал, что это происходит от C. Я просто проверил, что это разрешено и на Java. Это кажется странным, хотя: в моей интуиции запятая является разделителем, а не терминатором. Кроме того, можно опустить последнюю запятую. Итак, это терминатор, разделитель или и то, и другое? Но хорошо, эта функция доступна, и это хорошо знать. – Giorgio 12 August 2011 в 22:16
  • 4
    @Giorgio - исходный код для людей, а не для машин. Маленькие вещи, подобные этому, чтобы удержать нас от простых ошибок переноса, - это благословение, а не недосмотр. Для справки он также работает таким образом в PHP и ECMAScript (и, следовательно, в JavaScript и ActionScript), хотя он недопустим в нотации объектов JavaScript (JSON) (например, [1,2,3,] в порядке, но {a:1, b:2, c:3,} - нет). – Dereleased 12 August 2011 в 22:51
  • 5
    @Groky: Чем больше я думаю об этом, тем больше убеждаюсь, что синтаксис языка программирования должен быть максимально простым и последовательным и с минимальными исключениями: это облегчает изучение языка (меньше правил для запоминания ). Преимущество сохранения одного или двух нажатий клавиш при добавлении / удалении элемента в / из списка (что, кстати, я не делаю этого часто по сравнению с общим количеством времени, которое я провожу для кодирования) кажется довольно тривиальным для меня по сравнению с имеющий четко определенный синтаксис. – Giorgio 18 August 2011 в 07:10

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

#define LIST_BEGIN int a[] = {
#define LIST_ENTRY(x) x,
#define LIST_END };

Использование:

LIST_BEGIN
   LIST_ENTRY(1)
   LIST_ENTRY(2)
LIST_END

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

#define LIST_LAST_ENTRY(x) x

, и это будет очень неудобно использовать.

1
ответ дан Scott Langham 20 August 2018 в 23:22
поделиться

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

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

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

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

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

5
ответ дан Shafik Yaghmour 20 August 2018 в 23:22
поделиться

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

int a[] = {
  1,
  2,
  3, //You can delete this line and it's still valid
};
120
ответ дан Skilldrick 20 August 2018 в 23:22
поделиться
  • 1
    JavaScript поддерживает этот синтаксис: var a = [1, 2,];, поэтому большинство других языков я знаю ... ActionScript, Python, PHP. – Sean Fujiwara 14 August 2011 в 04:43
  • 2
    @Sean Это вызовет ошибку синтаксического разбора в IE JavaScript, так что будьте осторожны! – Skilldrick 15 August 2011 в 11:30
  • 3
    Это не для меня в IE9. Но он делает что-то странное ... он создает нулевой элемент. Я буду остерегаться. – Sean Fujiwara 16 August 2011 в 03:06
  • 4
    @Sean Извините, вы правы - это не ошибка синтаксического анализа в IE, но она добавит дополнительный элемент, установленный в undefined. – Skilldrick 16 August 2011 в 10:57
  • 5
    Самое разочаровывающее, JSON не поддерживает этот синтаксис. – Timmmm 19 January 2017 в 11:01

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

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

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

37
ответ дан vcsjones 20 August 2018 в 23:22
поделиться

Причина тривиальна: простота добавления / удаления строк.

Представьте следующий код:

int a[] = {
   1,
   2,
   //3, // - not needed any more
};

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

В отличие от других ответов, я действительно не считаю, что легкость генерации списка является веской причиной: в конце концов, для кода тривиально вставьте последнюю (или первую) строку. Генераторы кода записываются один раз и используются много раз.

6
ответ дан Vlad 20 August 2018 в 23:22
поделиться

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

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

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

4
ответ дан Yankes 20 August 2018 в 23:22
поделиться

Если вы используете массив без указанной длины, VC ++ 6.0 может автоматически идентифицировать его длину, поэтому, если вы используете «int a [] = {1,2,};» длина a равна 3 , но последний не был инициализирован, вы можете использовать «cout & lt;
-4
ответ дан zhi_jian 20 August 2018 в 23:22
поделиться
  • 1
    Является ли это ошибкой для VC6, которая не соответствует стандарту? – Thomson 18 September 2014 в 09:29
Другие вопросы по тегам:

Похожие вопросы: