也许我不是来自这个星球,但在我看来,以下应该是一个语法错误:

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

但事实并非如此。当这段代码在Visual Studio上编译时,我很惊讶,但我已经学会了不相信MSVC编译器,就c++规则而言,所以我检查了标准,它也是标准允许的。如果你不相信的话,语法规则可以参考8.5.1。

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.

那么,有什么特别的原因,这个多余的逗号是明确允许的吗?


当前回答

它允许每一行都遵循相同的形式。首先,这样可以更容易地添加新行,并让版本控制系统有效地跟踪更改,还可以更容易地分析代码。我想不出技术上的原因。

其他回答

这对机器来说更容易,比如解析和生成代码。 这对人类来说也更容易,即通过一致性进行修改、删除注释和视觉优雅。

假设C,你会这样写吗?

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

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

    return EXIT_SUCCESS
}

不。不仅因为最后的语句是一个错误,而且因为它是不一致的。那么为什么对收藏也要这样呢?即使在允许省略最后分号和逗号的语言中,社区通常也不喜欢这样做。例如,Perl社区似乎不喜欢省略分号,除了一行程序。他们也把这个应用到逗号上。

不要在多行集合中省略逗号,就像不要在多行代码块中省略分号一样。我是说,即使语言允许,你也不会这么做,对吧?对吧?

我看到了一个在其他答案中没有提到的用例, 我们最喜欢的宏:

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

添加宏到最后处理,将是巨大的痛苦。通过语法上的这个小变化,管理起来很简单。这比机器生成的代码更重要因为用图灵完备语言比用有限的预处理器要容易得多。

如果你这样做,它会很有用:

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

它使得生成数组或枚举的代码生成器更容易。

想象一下:

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("}")

它使生成代码更容易,因为您只需要添加一行,而不需要将添加最后一个条目视为特殊情况。在使用宏生成代码时尤其如此。有一种努力试图从语言中消除对宏的需求,但许多语言确实是与可用的宏一起发展的。额外的逗号允许定义和使用以下宏:

#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

用起来会很尴尬。