ここ数年、Cをあまり使用していません。 この質問 を読んだとき、私はなじみのないC構文に出会いました。
明らかに C99 では、次の構文が有効です。
void foo(int n) {
int values[n]; //Declare a variable length array
}
これは非常に便利な機能のようです。それをC++標準に追加することについて議論したことがありますか?
いくつかの潜在的な理由:
C++標準では、配列サイズは定数式でなければなりません(8.3.4.1)。
はい、もちろん、おもちゃの例ではstd::vector<int> values(m);
を使用できますが、これはスタックではなくヒープからメモリを割り当てます。そして、次のような多次元配列が必要な場合:
void foo(int x, int y, int z) {
int values[x][y][z]; // Declare a variable length array
}
vector
バージョンはかなり不格好になります。
void foo(int x, int y, int z) {
vector< vector< vector<int> > > values( /* Really painful expression here. */);
}
スライス、行、および列もメモリ全体に広がる可能性があります。
comp.std.c++
での議論を見ると、この質問が議論の両側にある非常にヘビーウェイトな名前とかなり議論の余地があることは明らかです。 std::vector
が常により良いソリューションであることは明らかではありません。
最近、これに関する議論がusenetで始まりました: なぜC++ 0xにVLAがないのか 。
スタック上に潜在的に大きな配列を作成する必要があることに同意していると思われる人々に同意します。引数は、サイズが事前にわかっている場合、静的配列を使用できます。また、サイズが事前にわからない場合は、安全でないコードを記述します。
C99 VLAは、スペースを無駄にせずに未使用の要素のコンストラクターを呼び出すことなく小さな配列を作成できるという小さな利点を提供できますが、型システムにかなり大きな変更を導入します(ランタイム値に応じて型を指定できる必要があります-これnew
演算子の型指定子を除いて、現在のC++にはまだ存在しませんが、それらは特別に扱われるため、ランタイムはnew
演算子のスコープをエスケープしません)。
std::vector
を使用できますが、動的メモリを使用し、独自のスタックアロケータを使用するのはまったく同じではありません(アライメントも問題です)。 VLAは固定サイズであるのに対し、ベクトルはサイズ変更可能なコンテナーであるため、同じ問題を解決することもできません。 C++ Dynamic Array の提案は、言語ベースのVLAの代替として、ライブラリベースのソリューションを導入することを目的としています。ただし、私の知る限り、C++ 0xの一部にはなりません。
必要に応じて、実行時に常にalloca()を使用してスタックにメモリを割り当てることができます。
void foo (int n)
{
int *values = (int *)alloca(sizeof(int) * n);
}
スタックに割り当てられるということは、スタックが巻き戻されると自動的に解放されることを意味します。
クイックノート:alloca(3)のMac OS Xのマニュアルページで述べたように、「alloca()関数はマシンとコンパイラに依存しています。その使用は推奨されていません。」ちょうどあなたが知っているので。
ヒープメモリの割り当てが、実行される操作に比べて非常に高価になる場合があります。例は行列演算です。 5から10個の要素のような小さな行列を使用して、多くの算術演算を行うと、mallocのオーバーヘッドが非常に大きくなります。同時に、サイズをコンパイル時定数にすることは、非常に無駄が多く柔軟性に欠けるようです。
C++自体は非常に安全ではないため、「安全でない機能を追加しない」という主張はあまり強くないと思います。一方、C++は間違いなく最もランタイム効率の高いプログラミング言語機能であるため、常に便利です。パフォーマンスが重要なプログラムを作成する人は、大部分はC++を使用し、可能な限りパフォーマンスが必要です。ヒープからスタックにデータを移動することは、そのような可能性の1つです。ヒープブロックの数を減らすことも別の方法です。 VLAをオブジェクトメンバーとして許可すると、これを実現する1つの方法になります。私はそのような提案に取り組んでいます。確かに実装するのは少し複雑ですが、かなり実行可能です。
私自身の仕事では、可変長の自動配列やalloca()のようなものが必要になるたびに、メモリが物理的にCPUスタック上にあることを気にしませんでした。一般的なヒープへの遅いトリップを引き起こさないスタックアロケーター。したがって、可変サイズのバッファをプッシュ/ポップできるメモリを所有するスレッドごとのオブジェクトがあります。一部のプラットフォームでは、これをmmu経由で拡張できます。他のプラットフォームには固定サイズがあります(mmuがないため、通常は固定サイズのCPUスタックも伴います)。私が使用しているプラットフォーム(ハンドヘルドゲームコンソール)には、とにかく貴重な小さなCPUスタックがあります。それは、それが不足している高速メモリに常駐しているためです。
CPUスタックに可変サイズのバッファーをプッシュする必要がないと言っているのではありません。正直に言って、これが標準ではないことに気付いたときは驚いた。確かに、この概念は言語に十分に適合するようだ。私にとっては、「可変サイズ」と「CPUスタック上に物理的に配置する必要がある」という要件が一緒になったことはありません。それは速度に関するものなので、私は独自の種類の「データバッファ用の並列スタック」を作成しました。
C++ 14で利用できるようです:
https://en.wikipedia.org/wiki/C%2B%2B14#Runtime-sized_one_dimensional_arrays
更新:C++ 14には含まれませんでした。
これは、C++/1xに含めるために考慮されました ただし、削除されました (これは以前に言ったことに対する修正です)。
とにかく、この役割を果たすstd::vector
が既にあるので、C++ではあまり役に立ちません。
これにはstd :: vectorを使用します。例えば:
std::vector<int> values;
values.resize(n);
メモリはヒープに割り当てられますが、これにはパフォーマンス上の小さな欠点しかありません。さらに、サイズがかなり制限されているため、スタックに大きなデータブロックを割り当てないことが賢明です。
C99はVLAを許可します。また、VLAの宣言方法に制限を設けています。詳細については、標準の6.7.5.2を参照してください。 C++はVLAを許可しません。しかし、g ++では許可されています。
このような配列はC99の一部ですが、標準C++の一部ではありません。他の人が言ったように、ベクトルは常にはるかに優れたソリューションです。これがおそらく可変サイズの配列がC++標準(または提案されたC++ 0x標準)にない理由です。
ところで、「なぜ」C++標準が現状なのかという質問については、モデレートされたUsenetニュースグループ comp.std.c ++ にアクセスしてください。