353

私はこの惑星の出身ではないかもしれませんが、以下は構文エラーであるように思われます。

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

しかし、そうではありません。このコードを Visual Studio でコンパイルしたときは驚きましたが、C++ の規則に関する限り、MSVC コンパイラを信頼しないことを学んだので、標準を確認したところ、標準で許可されています。信じられないなら、文法規則については 8.5.1 を参照してください。

ここに画像の説明を入力

なぜこれが許可されているのですか?これはばかげた役に立たない質問かもしれませんが、私が尋ねている理由を理解していただきたいです。それが一般的な文法規則のサブケースである場合、私は理解するでしょう-初期化子リストの最後にある冗長なコンマを許可しないためだけに、一般的な文法をこれ以上難しくしないことに決めました。いいえ、追加のコンマは明示的に許可されています。たとえば、関数呼び出しの引数リストの末尾に冗長なコンマを含めることは許可されていません (関数が を受け取る場合...) 。これは通常のです。

繰り返しになりますが、この冗長なコンマが明示的に許可されている特定の理由はありますか?

4

20 に答える 20

448

これにより、ソースコードの生成が容易になり、後日簡単に拡張できるコードを記述できるようになります。次の項目にエントリを追加するために必要なものを検討してください。

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

...既存の行にカンマを追加し、新しい行追加する必要があります。これを、3つの後にすでにコンマがあり、行を追加するだけの場合と比較してください。同様に、行を削除したい場合は、それが最後の行であるかどうかを気にせずに削除でき、コンマをいじることなく行を並べ替えることができます。基本的に、それはあなたが線を扱う方法に均一性があることを意味します。

次に、コードの生成について考えます。(擬似コード)のようなもの:

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

あなたが書いている現在のアイテムが最初か最後かを心配する必要はありません。はるかに簡単です。

于 2011-08-12T16:39:46.553 に答える
130

次のようなことをすると便利です。

int a[] = {
  1,
  2,
  3, //You can delete this line and it's still valid
};
于 2011-08-12T16:39:52.560 に答える
37

開発者にとって使いやすさだと思います。

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

さらに、何らかの理由でコードを生成するツールがある場合は、ツールは、それが初期化の最後の項目であるかどうかを気にする必要はありません。

于 2011-08-12T16:40:57.763 に答える
31

私はいつも、追加の要素を追加するのが簡単になると思っていました:

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

単純に次のようになります。

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

後日。

于 2011-08-12T16:39:34.207 に答える
21

行の追加/削除/生成の容易さについて誰もが言っていることはすべて正しいですが、この構文が真価を発揮するのは、ソース ファイルを一緒にマージするときです。次の配列があるとします。

int ints[] = {
    3,
    9
};

そして、このコードをリポジトリにチェックインしたとします。

次に、あなたの仲間がそれを編集し、最後に追加します:

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

そして同時に編集し、最初に追加します:

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

意味的には、これらの種類の操作 (最初に追加する、最後に追加する) は完全にマージセーフである必要があり、バージョン管理ソフトウェア (できれば git) は自動マージできる必要があります。悲しいことに、あなたのバージョンには 9 の後にコンマがなく、あなたの友人のバージョンにはあるため、これは当てはまりません。一方、元のバージョンの末尾が 9 の場合、それらは自動マージされます。

したがって、私の経験則は次のとおりです。リストが複数行にまたがる場合は末尾のコンマを使用し、リストが1行にある場合は使用しないでください。

于 2011-08-16T17:41:54.720 に答える
16

Annotated C++ Reference Manual ( ARM ) を誰も引用していないことに驚いています。 [dcl.init]について次のように述べています。

初期化の表記法が明らかに多すぎますが、それぞれが特定の使用スタイルに適しているようです。={initializer_list,opt}表記は C から継承されもので、データ構造と配列の初期化に適しています。[...]

ARMが作成されて以来、文法は進化していますが、その起源は残っています。

C99 の論理的根拠に移動して、C でこれが許可された理由を確認すると、次のように書かれています。

K&R では、initializer-list の末尾にある初期化子の末尾にコンマを使用できます。標準では、初期化子リストにメンバーを追加または削除する際の柔軟性が提供され、そのようなリストのマシン生成が簡素化されるため、この構文が保持さ れています。

于 2015-10-19T11:59:16.273 に答える
15

下位互換性の理由から、末尾のコンマは許可されていると思います。主に自動生成された多くの既存のコードがあり、末尾にコンマが挿入されます。最後に特別な条件のないループを書きやすくします。例えば

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

プログラマーにとっては何のメリットもありません。

PS この方法でコードを自動生成する方が簡単ですが、実際には常に末尾のコンマを入れないように注意しました。労力は最小限で、読みやすさが向上し、それがより重要です。一度コードを書くと、それを何度も読むことになります。

于 2011-08-12T16:47:58.690 に答える
13

私の知る限り、これが許可されている理由の1つは、コードを自動的に生成するのは簡単でなければならないということです。最後の要素に特別な処理は必要ありません。

于 2011-08-12T16:41:40.327 に答える
12

これにより、配列または列挙型を吐き出すコード ジェネレーターがより簡単になります。

想像:

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("}")
于 2011-08-12T16:46:58.160 に答える
6

その理由は些細なことです。行の追加/削除のしやすさです。

次のコードを想像してみてください。

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

これで、末尾のコンマを時々追加/削除しなくても、リストにアイテムを簡単に追加/削除できます。

他の回答とは対照的に、リストの生成が簡単であることが正当な理由であるとは思いません。結局のところ、コードが最後(または最初)の行を特殊なケースにするのは簡単です。コードジェネレーターは一度作成され、何度も使用されます。

于 2011-08-12T16:40:47.593 に答える
6

これにより、すべての行が同じ形式に従うことができます。まず、これにより、新しい行の追加が容易になり、バージョン管理システムで変更を有意義に追跡できるようになります。また、コードをより簡単に分析できるようになります。技術的な理由が思いつかない。

于 2011-08-12T16:40:52.863 に答える
6

実際*に許可されていない唯一の言語は Javascript であり、無数の問題を引き起こします。たとえば、配列の中央から行をコピーして貼り付け、最後に貼り付け、コンマを削除するのを忘れた場合、IE 訪問者にとってサイトは完全に壊れます。

※理論上は許容されますが、Internet Explorer は規格に準拠しておらず、エラーとして扱います

于 2011-08-12T18:37:15.930 に答える
6

コードの解析と生成など、マシンにとってはより簡単です。人間にとっても簡単です。つまり、変更、コメントアウト、および一貫性による視覚的なエレガンスです。

Cを仮定して、次のように書きますか?

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

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

    return EXIT_SUCCESS
}

いいえ。最後のステートメントがエラーであるだけでなく、一貫性がないためです。では、なぜコレクションに対して同じことをするのでしょうか? 最後のセミコロンとコンマを省略できる言語でさえ、コミュニティは通常それを好まない。たとえば、Perl コミュニティは、セミコロンを省略したり、ワンライナーを禁止したりすることを好まないようです。彼らはそれをカンマにも適用します。

複数行のコード ブロックでセミコロンを省略しないのと同じ理由で、複数行のコレクションでカンマを省略しないでください。言葉が許してもやらないでしょ?右?

于 2011-08-12T23:17:11.040 に答える
5

これにより、長いリスト内で要素を移動することによって引き起こされるミスを防ぐことができます。

たとえば、次のようなコードがあるとします。

#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 サイトの最初の 3 部作を示しているので、これは素晴らしいことです。

Stack Overflow
Super User
Server Fault

しかし、それには1つの問題があります。ご覧のとおり、この Web サイトのフッターには、スーパー ユーザーの前にサーバー フォールトが表示されます。誰かが気付く前にそれを修正した方がよいでしょう。

#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」という Web サイトはありませんが、コンパイラは存在すると主張しています。-ここでの問題は、C には文字列連結機能があり、2 つの二重引用符で囲まれた文字列を記述し、それらを何も使用せずに連結できることです (符号には複数の意味があるため、整数でも同様の問題が発生する可能性があります)。

元の配列の末尾に不要なカンマがあった場合はどうなるでしょうか。まあ、行は移動しますが、そのようなバグは発生しませんでした. コンマのような小さなものを見逃すのは簡単です。すべての配列要素の後にコンマを置くことを覚えていれば、そのようなバグは起こりません。コンマが問題の原因であることがわかるまで、何かをデバッグするために 4 時間を無駄にしたくないでしょう。

于 2014-05-10T16:56:38.737 に答える
4

多くのものと同様に、配列初期化子の末尾のコンマは、C++ が C から継承したものの 1 つです (そして、今後もサポートする必要があります)。ここに置かれているものとはまったく異なるビューが、書籍「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 の自動生成が容易になるというものです。列挙型宣言や単一の宣言内の複数の変数宣言子など、すべてのコンマ区切りリストで末尾のコンマが許可されている場合、主張はより信頼できます。ではない。

...私にとっては、これはより理にかなっています

于 2015-07-30T14:16:59.303 に答える
2

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.

于 2011-08-16T21:03:01.990 に答える
1

追加する必要があるのは 1 行だけで、最後のエントリの追加を特別な場合のように扱う必要がないため、コードの生成が簡単になります。これは、マクロを使用してコードを生成する場合に特に当てはまります。言語からマクロの必要性を排除しようとする動きがありますが、多くの言語はマクロが利用可能になるのと並行して進化しました。追加のコンマにより、次のようなマクロを定義して使用できます。

#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

それは非常に使いにくいでしょう。

于 2018-04-23T12:15:49.117 に答える
-4

長さを指定せずに配列を使用すると、VC++6.0 はその長さを自動的に識別できます。初期化されているので、「cout<

于 2011-08-18T01:10:29.027 に答える