目次
はじめに
C++ 標準ライブラリの一部である C++ 標準テンプレートライブラリ STL は、さまざまな種類のコンテナに対してアルゴリズムを適用するためのフレームワークを提供している。しかしながら、通常の配列は STL コンテナのインターフェイスには備わっていない(STL コンテナのイテレータとしてのインターフェイスは用意されているけれども)。
通常の配列を置き換えるものとして、STL では vector<>
が提供されているが、vector<>
は動的配列のセマンティクスを持つので、要素数が変化する可能性を持つデータを管理対象とする。静的なサイズさえあれば充分な場面では、このことはいくぶんかのオーバーヘッドを生じさせることとなる。
Matthew H. Austern は彼の本、 Generic Programming and the STL の中で、 block と名づけられた、静的サイズの通常の配列を扱うとても便利なラッパクラスを紹介している。これは通常の配列よりも安全であり、パフォーマンスもひけを取らない。 The C++ Programming Language, 3rd edition で、Bjarne Stroustrup は c_array という同じようなクラスを紹介しているが、これは私(Nicolai Josuttis)が、私の本 The C++ Standard Library - A Tutorial and Reference の中で carray と呼んでいるものを少々変更して提供したものだ。ここに紹介するのは、これらのアプローチのエッセンスを、boost からのたくさんのフィードバックで味付けしたものである。
いろいろな名前を考えたすえ、このクラスの名前はシンプルに array と決定した。
インタフェース
このクラスは以下のインターフェイスを提供する。
型:
型 | 説明 |
---|---|
value_type | 要素の型 |
iterator | イテレータの型(ランダムアクセス・イテレータ) |
const_iterator | 定数とされる要素へのイテレータの型 |
reference | 要素の参照の型 |
const_reference | 定数とされる要素への参照の型 |
size_type | 符号付のサイズの型 |
difference_type | 符号なしの距離(差分)の型 |
操作:
操作 | 説明 |
---|---|
array<type,num> | デフォルトコンストラクタ。num (要素数)個のtype (型)を要素とする配列を作成する。下のコメントを参照。 |
array<type,num>(a) | コピーコンストラクタa のすべての要素をコピーする。(a は同じ type と num でなければならない)。 |
operator= | 代入。すべての要素を代入する。 |
assign(val) | すべての要素に val を代入する。 |
begin() | 最初の要素へのイテレータを返す。 |
end() | 最後の要素の次の位置へのイテレータを返す。 |
rbegin() | 逆イテレーションでの最初の要素を指す逆イテレータを返す。 |
rend() | 逆イテレーションでの最後の要素のひとつ後ろ(behind)を指す逆イテレータを返す。 |
operator[i] | インデックス i の要素を返す(範囲チェックなし) |
at(i) | インデックス i の要素を返す(i が不正な場合は std::range_error を送出する) |
front() | 最初の要素を返す(要素が存在することを呼び出し側で保証しなければならない) |
back() | 最後の要素を返す(要素が存在することを呼び出し側で保証しなければならない) |
data() | 生の配列を読み取り専用として返す。 |
size() | 要素数を返す。 |
empty() | 配列が空かどうかを返す。 |
max_size() | 格納可能な要素数の最大値を返す(size() と同じ)。 |
swap(a) | array a と要素を交換する。 |
==, != | 等値性を調べる。 |
<, <=, >, >= | array の比較演算。 |
値:
定数 | 説明 |
---|---|
static_size | コンパイル時の要素数 |
議論
array
クラスは"reversible container"(C++ 標準 Section 23.1, [lib.container.requirements] を参照)の要件のほとんどを満たしているが、完全にではない。array
が reversible な STL コンテナではない理由は以下のとおりである。
- 提供されるべきコンストラクタがない
- 要素が不確定な初期値を持つ可能性がある(以下を参照)。
swap()
の計算量が定数ではない。size()
が常に一定で、型の第2テンプレート引数によって決定される。- アロケータのサポートを提供しないコンテナである。
また、"sequence"(C++標準Section 23.1.1, [lib.sequence.reqmts]参照)であるための要件を、以下のものを除いて満たしていない。
front()
とback()
が提供されている。operator[]
とat()
が提供されている。
コンストラクタの点に関しては、重要な設計上のトレードオフを行い、(それによって)"aggregate"( C++ 標準 Section 8.5.1, [dcl.init.aggr] を参照)として array
を実装することができた。つまり、
array
は、ブレースで囲まれ、要素番号の昇順に並べられ、カンマで区切られたコンテナの要素への初期値のリストによって初期化することができる。boost::array<int,4> a = { { 1, 2, 3 } };
- リスト内の初期値の数が足りない場合、残りの要素はデフォルト値のままになる(したがって定義された値を持つ)ことに注意しよう。
- しかしながら、
初期値のリストをないままにすると、要素は不確定の初期値をもつことになる。
- ユーザー定義のコンストラクタを持たない。
private
あるいはprotected
な 非static
データメンバをもたない。- 基底クラスを持たない。
- 仮想関数を持たない。
現在の実装はこのようなアプローチをとっているが、不確定な初期値を持つ可能性があることは大きな問題点である。そこで、この点に関してこのようにすべきだというあなたの考えを、ぜひフィードバックしてほしい。ここから導かれる 未解決の問題点 は以下のようなものだ。
- 初期化リストをサポートするべきか、あるいは次のようなものでOKか?
int data[] = { 1, 2, 3, 4 }
array<int,5> x(data);
あるいはarray<int,data> x;
- 可搬性のために、初期化リストとして、"
{ { ... } }
"のかわりに、"{ ... }
"を使えるようにするべきだろうか?- 規格の 8.5.1 (11) ではこれは許されているようだが、gcc 2.95.2 では警告メッセージを表示する。
- 初期値の決定と、初期化リストのサポートのための、別の方法があるだろうか?
- 逆イテレータのstuffのための Static_casts はどうするか?
- 原文は「Static_casts for reverse iterator stuff?」。よくわかりません…
建設的なフィードバックはどのようなものでも歓迎する。注意してほしいのは、boostメーリングリストのすべてのメールを読むだけの時間が、私にはないという点だ。というわけで、確実にフィードバックが私に届くようにするため、このクラスに関するメールについては、私にコピーを送ってほしい。
コード例
以下のコードは「このままの形(as is)」で提供され、明示的あるいは暗黙的な保証はない。
- array.hpp,
array<>
の実装ファイル - array1.cpp,
array<>
を使った簡単な例 - array2.cpp,
array<>
を使った別の例 - array3.cpp,
array<>
を使った三つ目の例 - array4.cpp,
array<>
のarray<>
を使った例 - array5.cpp,
array<>
の他の操作をテストする例