前回の記事を書いた2日後くらいに、よく考えると先のコードはまずいのではという気もちになった。今日まで放置していたのは、まず自分が書いていたコードを直すことと、書かなければならない書類をこなすことに集中していたからだ。書類は数日前に草稿を共著者に送りつけた。穴だらけだとわかっているので、そろそろまた仕事が帰ってくるだろうが、今この瞬間は少し落ち着けている。もっと仕事を早くこなせるようにならねばならない。
さて、何がまずいかもしれないかというと、まずstd::vector
は(C++17以前は)要素型Tが完全形であることを要求している。よって、以下のような型はおかしいのではないか。
struct X
{
std::vector<X> xs;
};
ここで、struct X
の宣言が終わる最後の行まで、Xは完全形ではない。しかしその内部でstd::vector<X>
を使っている。ここではまだXは不完全型ではないのか。
ところでこれは動く。X x; x.xs.resize(10);
とかしても動く。そもそも気づかずに書いてしまった理由は、手元のいくつかのコンパイラで動いたからというのが大きい(C++erとしてはまだまだ未熟だ)。それに、書いているときはstd::vector
は内部的にはヒープにアロケートした配列へのポインタ、つまりここでX*
のようなものを持っているに違いないと思っていたため、動くだろうと考えたのだった。
だが実際には上記のように、std::vector
などコンテナの要素型は完全型でなければならない。ので上のコードが動いたのは、使ったSTL実装が動くようなものだったからで、つまるところ処理系依存な挙動をしたのだと思われる。ちなみに、C++17では不完全型のstd::vector
が作れるようになる。その代わり、アロケータ型が「アロケータ完全性要求」なるものを満たしている必要があるようだが。
よって、以上のような型を書いてはならず、C++17まで待つか、Boost.containerを使うとよい。これは不完全型をサポートしている。
上記リンクから飛ぶこともできるが、以下の記事で標準ライブラリと不完全型の話がなされている。http://www.drdobbs.com/the-standard-librarian-containers-of-inc/184403814