int a[] = {1,2,}; Why is a trailing comma in an initializer-list allowed?

C++SyntaxGrammarLanguage Lawyer

C++ Problem Overview


Maybe I am not from this planet, but it would seem to me that the following should be a syntax error:

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

But it's not. I was surprised when this code compiled on Visual Studio, but I have learnt not to trust MSVC compiler as far as C++ rules are concerned, so I checked the standard and it is allowed by the standard as well. You can see 8.5.1 for the grammar rules if you don't believe me.

enter image description here

Why is this allowed? This may be a stupid useless question but I want you to understand why I am asking. If it were a sub-case of a general grammar rule, I would understand - they decided not to make the general grammar any more difficult just to disallow a redundant comma at the end of an initializer list. But no, the additional comma is explicitly allowed. For example, it isn't allowed to have a redundant comma in the end of a function-call argument list (when the function takes ...), which is normal.

So, again, is there any particular reason this redundant comma is explicitly allowed?

C++ Solutions


Solution 1 - C++

It makes it easier to generate source code, and also to write code which can be easily extended at a later date. Consider what's required to add an extra entry to:

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

... you have to add the comma to the existing line and add a new line. Compare that with the case where the three already has a comma after it, where you just have to add a line. Likewise if you want to remove a line you can do so without worrying about whether it's the last line or not, and you can reorder lines without fiddling about with commas. Basically it means there's a uniformity in how you treat the lines.

Now think about generating code. Something like (pseudo-code):

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

No need to worry about whether the current item you're writing out is the first or the last. Much simpler.

Solution 2 - C++

It's useful if you do something like this:

int a[] = {
  1,
  2,
  3, //You can delete this line and it's still valid
};

Solution 3 - C++

Ease of use for the developer, I would think.

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

Additionally, if for whatever reason you had a tool that generated code for you; the tool doesn't have to care about whether it's the last item in the initialize or not.

Solution 4 - C++

I've always assumed it makes it easier to append extra elements:

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

simply becomes:

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

at a later date.

Solution 5 - C++

Everything everyone is saying about the ease of adding/removing/generating lines is correct, but the real place this syntax shines is when merging source files together. Imagine you've got this array:

int ints[] = {
    3,
    9
};

And assume you've checked this code into a repository.

Then your buddy edits it, adding to the end:

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

And you simultaneously edit it, adding to the beginning:

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

Semantically these sorts of operations (adding to the beginning, adding to the end) should be entirely merge safe and your versioning software (hopefully git) should be able to automerge. Sadly, this isn't the case because your version has no comma after the 9 and your buddy's does. Whereas, if the original version had the trailing 9, they would have automerged.

So, my rule of thumb is: use the trailing comma if the list spans multiple lines, don't use it if the list is on a single line.

Solution 6 - C++

I am surprised after all this time no one has quoted the Annotated C++ Reference Manual(ARM), it says the following about [dcl.init] with emphasis mine:

>There are clearly too many notations for initializations, but each seems to serve a particular style of use well. The ={initializer_list,opt} notation was inherited from C and serves well for the initialization of data structures and arrays. [...]

although the grammar has evolved since ARM was written the origin remains.

and we can go to the C99 rationale to see why this was allowed in C and it says:

> K&R allows a trailing comma in an initializer at the end of an > initializer-list. The Standard has retained this syntax, since it > provides flexibility in adding or deleting members from an initializer > list, and simplifies machine generation of such lists.

Solution 7 - C++

Trailing comma I believe is allowed for backward compatibility reasons. There is a lot of existing code, primarily auto-generated, which puts a trailing comma. It makes it easier to write a loop without special condition at the end. e.g.

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

There isn't really any advantage for the programmer.

P.S. Though it is easier to autogenerate the code this way, I actually always took care not to put the trailing comma, the efforts are minimal, readability is improved, and that's more important. You write code once, you read it many times.

Solution 8 - C++

One of the reasons this is allowed as far as I know is that it should be simple to automatically generate code; you don't need any special handling for the last element.

Solution 9 - C++

I see one use case that was not mentioned in other answers, our favorite Macros:

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

Adding macros to handle last , would be big pain. With this small change in syntax this is trivial to manage. And this is more important than machine generated code because is usually lot of easier to do it in Turing complete langue than very limited preprocesor.

Solution 10 - C++

It makes code generators that spit out arrays or enumerations easier.

Imagine:

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

I.e., no need to do special handling of the first or last item to avoid spitting the trailing comma.

If the code generator is written in Python, for example, it is easy to avoid spitting the trailing comma by using str.join() function:

print("enum Items {")
print(",\n".join(items))
print("}")

Solution 11 - C++

The reason is trivial: ease of adding/removing lines.

Imagine the following code:

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

Now, you can easily add/remove items to the list without having to add/remove the trailing comma sometimes.

In contrast to other answers, I don't really think that ease of generating the list is a valid reason: after all, it's trivial for the code to special-case the last (or first) line. Code-generators are written once and used many times.

Solution 12 - C++

It allows every line to follow the same form. Firstly this makes it easier to add new rows and have a version control system track the change meaningfully and it also allows you to analyze the code more easily. I can't think of a technical reason.

Solution 13 - C++

The only language where it's - in practice* - not allowed is Javascript, and it causes an innumerable amount of problems. For example if you copy & paste a line from the middle of the array, paste it at the end, and forgot to remove the comma then your site will be totally broken for your IE visitors.

*In theory it is allowed but Internet Explorer doesn't follow the standard and treats it as an error

Solution 14 - C++

It's easier for machines, i.e. parsing and generation of code. It's also easier for humans, i.e. modification, commenting-out, and visual-elegance via consistency.

Assuming C, would you write the following?

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

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

    return EXIT_SUCCESS
}

No. Not only because the final statement is an error, but also because it's inconsistent. So why do the same to collections? Even in languages that allow you to omit last semicolons and commas, the community usually doesn't like it. The Perl community, for example, doesn't seem to like omitting semicolons, bar one-liners. They apply that to commas too.

Don't omit commas in multiline collections for the same reason you don't ommit semicolons for multiline blocks of code. I mean, you wouldn't do it even if the language allowed it, right? Right?

Solution 15 - C++

This is allowed to protect from mistakes caused by moving elements around in a long list.

For example, let's assume we have a code looking like this.

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

And it's great, as it shows the original trilogy of Stack Exchange sites.

Stack Overflow
Super User
Server Fault

But there is one problem with it. You see, the footer on this website shows Server Fault before Super User. Better fix that before anyone notices.

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

After all, moving lines around couldn't be that hard, could it be?

Stack Overflow
Server FaultSuper User

I know, there is no website called "Server FaultSuper User", but our compiler claims it exists. Now, the issue is that C has a string concatenation feature, which allows you to write two double quoted strings and concatenate them using nothing (similar issue can also happen with integers, as - sign has multiple meanings).

Now what if the original array had an useless comma at end? Well, the lines would be moved around, but such bug wouldn't have happened. It's easy to miss something as small as a comma. If you remember to put a comma after every array element, such bug just cannot happen. You wouldn't want to waste four hours debugging something, until you would find the comma is the cause of your problems.

Solution 16 - C++

Like many things, the trailing comma in an array initializer is one of the things C++ inherited from C (and will have to support for ever). A view totally different from those placed here is mentioned in the book "Deep C secrets".

Therein after an example with more than one "comma paradoxes" :

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? */
};

we read :

>...that trailing comma after the final initializer is not a typo, but a blip in the syntax carried over from aboriginal C. Its presence or absence is allowed but has no significance. The justification claimed in the ANSI C rationale is that it makes automated generation of C easier. The claim would be more credible if trailing commas were permitted in every comma-sepa-rated list, such as in enum declarations, or multiple variable declarators in a single declaration. They are not.

... to me this makes more sense

Solution 17 - C++

In addition to code generation and editing ease, if you want to implement a parser, this type of grammar is simpler and easier to implement. C# follows this rule in several places that there's a list of comma-separated items, like items in an enum definition.

Solution 18 - C++

It makes generating code easier as you only need to add one line and don't need to treat adding the last entry as if it's a special case. This is especially true when using macros to generate code. There's a push to try to eliminate the need for macros from the language, but a lot of the language did evolve hand in hand with macros being available. The extra comma allows macros such as the following to be defined and used:

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

Usage:

LIST_BEGIN
   LIST_ENTRY(1)
   LIST_ENTRY(2)
LIST_END

That's a very simplified example, but often this pattern is used by macros for defining things such as dispatch, message, event or translation maps and tables. If a comma wasn't allowed at the end, we'd need a special:

#define LIST_LAST_ENTRY(x) x

and that would be very awkward to use.

Solution 19 - C++

So that when two people add a new item in a list on separate branches, Git can properly merge the changes, because Git works on a line basis.

Solution 20 - C++

If you use an array without specified length,VC++6.0 can automaticly identify its length,so if you use "int a[]={1,2,};"the length of a is 3,but the last one hasn't been initialized,you can use "cout<

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionArmen TsirunyanView Question on Stackoverflow
Solution 1 - C++Jon SkeetView Answer on Stackoverflow
Solution 2 - C++SkilldrickView Answer on Stackoverflow
Solution 3 - C++vcsjonesView Answer on Stackoverflow
Solution 4 - C++Oliver CharlesworthView Answer on Stackoverflow
Solution 5 - C++amossView Answer on Stackoverflow
Solution 6 - C++Shafik YaghmourView Answer on Stackoverflow
Solution 7 - C++Gene BushuyevView Answer on Stackoverflow
Solution 8 - C++Fredrik PihlView Answer on Stackoverflow
Solution 9 - C++YankesView Answer on Stackoverflow
Solution 10 - C++Maxim EgorushkinView Answer on Stackoverflow
Solution 11 - C++VladView Answer on Stackoverflow
Solution 12 - C++Mark BView Answer on Stackoverflow
Solution 13 - C++Thomas BoniniView Answer on Stackoverflow
Solution 14 - C++LouisView Answer on Stackoverflow
Solution 15 - C++Konrad BorowskiView Answer on Stackoverflow
Solution 16 - C++Nikos AthanasiouView Answer on Stackoverflow
Solution 17 - C++IravanchiView Answer on Stackoverflow
Solution 18 - C++Scott LanghamView Answer on Stackoverflow
Solution 19 - C++noɥʇʎԀʎzɐɹƆView Answer on Stackoverflow
Solution 20 - C++zhi_jianView Answer on Stackoverflow