char配列を使用したこれらの新しい式のどれが整形式ですか?
次のプログラムの場合:
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
では、このコードが整形式であるかどうかについて、言語は何と言っていますか?
現在のルールを知りたいのですが、以前のバージョンの言語でこれが変更されたかどうかも知りたいです。
回答
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を不整形にします。
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";
ヌル文字を許可および無視します。)