char配列を使用したこれらの新しい式のどれが整形式ですか?

Aug 23 2020

次のプログラムの場合:

int main() 
{
    new char[4] {"text"};  // #1
    new char[5] {"text"};  // #2
    new char[] {"text"};   // #3
}

clangは次のようなエラーを出します#1

error: initializer-string for char array is too long

そして受け入れ#2#3

gccは、すべてのステートメントに対して次のエラーを出します。

error: invalid conversion from 'const char*' to 'char' [-fpermissive]

それに加えて#3、エラーが発生します。

error: expected primary-expression before ']' token

では、このコードが整形式であるかどうかについて、言語は何と言っていますか?

現在のルールを知りたいのですが、以前のバージョンの言語でこれが変更されたかどうかも知りたいです。

回答

11 NicolBolas Aug 23 2020 at 20:21

OK、これは非常に簡単に追跡できます。の存在{}は、リストの初期化が実行されていることを意味するため、仕様のお気に入りの部分[dcl.init.list] / 3にアクセスします。

ケース1で初期化されるオブジェクトはchar[4]です。braced-init-listは指定された初期化子ではないため、3.1は無視されます。char[4]はクラスではないため、3.2は無視されます。それは3.3に私たちをもたらします:

それ以外の場合、Tが文字配列であり、初期化子リストに適切に型指定された文字列リテラル([dcl.init.string])である単一の要素がある場合、初期化はその節で説明されているように実行されます。

まあ、それchar[4]は間違いなく文字配列であり、初期化子リストには間違いなく単一の要素が含まれており、その要素は実際には文字配列のタイプと一致します。それでは、[dcl.init.string]に進みます。

それは私たちに(ファッションの後)教えてくれます:

文字列リテラルの値の連続する文字は、配列の要素を初期化します。

しかし、次の段落は警告します:

配列要素よりも多くの初期化子があってはなりません。

まあ、それは#1を不整形にします。

したがって、のプロセスをやり直しますchar[5]。5は十分に大きいので、それはトリガーされません。

最後に、になりchar[]ます。初期化に関する限り、これは数値を使用することと同じです。char[]は文字の配列であるため、上記の規則に従います。C ++ 17には、使用上のチョークなりchar[]new表現が、C ++ 20はそれで結構です。

type-idまたはnew-type-idが未知の境界の配列タイプ([dcl.array])を示す場合、new-initializerは省略されません。割り当てられたオブジェクトは、n個の要素を持つ配列です。ここで、nは、new-initializer([dcl.init.aggr]、[dcl.init.string])で指定された初期要素の数から決定されます。

つまり、#2と#3は合法であると想定されています。したがって、GCCはそれらを不正な形式にするのは間違っています。そして、それは間違った理由で#1を不整形にします。

1 aschepler Aug 23 2020 at 20:30

Clangは、#1の形式が正しくなく、#2が問題ないという点で正しいです。

Ted Lyngmoがコメントで述べたように、紙P1009R2がそれを許可するように変更するまで、#3はC ++文法規則によって無効でした。new式は、単に空のpossiblity許可しなかった[]ことにより、作成された配列初期化する構文がなかったときから残っタイプで、新しい表現を実際のサイズを決定するために、コンパイラのために、したがって、方法はありませんが。このペーパーの変更はC ++ 20で受け入れられます(ただし、コンパイラーの作成者は、以前の-std=モードで「修正」をさかのぼってサポートすることを選択する場合があります)。

#1と#2の違いについては、配列オブジェクトの初期化を[expr.new]で指定して、[dcl.init]の直接初期化ルールに従います。初期の[dcl.init]での初期化の一般的な規則は、初期化子がbraced-init_listである場合、それはlist-initializationであると言います。[dcl.init.list]のこのルールは次のようになります。

オブジェクトまたはタイプの参照のリスト初期化は、T次のように定義されます。

  • [C ++ 20のみ:] braced-init-list指定された初期化子リストが含まれている場合、..。

  • Tが集約クラスである場合...

  • それ以外の場合、Tが文字配列であり、初期化子リストに適切に型指定された文字列リテラル([dcl.init.string])である単一の要素がある場合、初期化はその節で説明されているように実行されます。

  • ..。

したがって、[dcl.init.string](C ++ 17、latest)は、このコードに適用される実際の初期化ルールを示します。

{C ++ 17:狭い文字タイプ} {C ++ 20:通常の文字タイプ([basic.fundamental])}、char8_­t配列、char16_­t配列、char32_­t配列、またはwchar_­t配列の配列は、{C ++ 17:aナロー} {C ++ 20:通常の}文字列リテラル、UTF-8文字列リテラル、UTF-16文字列リテラル、UTF-32文字列リテラル、またはワイド文字列リテラル、または適切に入力された文字列リテラルブレース([lex.string])。文字列リテラルの値の連続する文字は、配列の要素を初期化します。

配列要素よりも多くの初期化子があってはなりません。[例:

char cv[4] = "asdf";            // error

暗黙の末尾のスペースがないため、形式が正しくありません'\0'。—終了例]

配列要素よりも初期化子の数が少ない場合、明示的に初期化されていない各要素はゼロで初期化されます([dcl.init])。

プレーン変数の定義と同様に、new-expressionの文字配列型に指定された境界がある場合、それを初期化する文字列リテラルのすべての文字(末尾のヌル文字を含む)に対して十分な大きさである必要があります。

(これはCとC ++の古い違いです。Cはchar cv[4] = "asdf";ヌル文字を許可および無視します。)