22 ます const の意味は簡単で、 cols という変数の持つ値は、 第 2 章ループとカウント こで定義した後にはプログラムでは変えないと いうことでした。 std : : string : : size-type という型は難しく見えるかもしれません。 こで : : はスコープ演 算子とよばれるもので、 std::stirng は、 std という名前空間の stri Ⅱ g の完全な名前でした。名前空間やプ ロックと同じく、クラスも独自のスコープを持ちます。 std : : stirng が「保持する文字列の文字数を格納する ための変数」の型として、 size_type というものを定義しているのです。 string の文字数を格納するために は、いつでも std::string::size-type という型の変数を使うべきなのです。 cols の型を std::string::size-tYPe にした理由は、 greeting に格納されている文字列がどんなに長く なっても、 cols にその長さを格納したいと考えたからです。 cols の型としては単純に整数の int を考えるか もしれません。実際、それで大抵はうまくいくでしよう。しかし、 cols に格納される値は、ユーザのこのプロ グラムへの入力によって決まるもので、その長さを私たちがコントールする方法はないのです。誰かが int の 範囲を超えるほど長い入力をする可能性はあるのです。 int は rows の型としては十分でした。 rows は pad によって決まり、それはプログラマが決めるものだから です。どのような環境でも int は、最低でも 32767 という大きな整数までが使えるように決められています。 しかし、ライプラリが特別な目的で定義している型は、その目的に使うような習慣をつけたほうがよいでしよう。 まず string が負の数の文字数を持っということはありません。そのため、 std::string::size-type は符 号なし (unsigned) の型とよばれます。これは負の数を格納できないという意味です。この性質は今のプログラ ムでは使われませんが、 8.1.3 では極めて重要になる例を見ます。 何行出力するかがわかったので、またⅶ ile 文を使うことができます。 std: : string: : size—type c / / 不変な表明 : 現在の行で出力した文字数は c while (c ! = cols) { / / ここに文字を出力する部分を書く / / 不変な表明が正しくなるように c を調整する この while は 2.3 のものとほば同じように動作しますが、中身に違う点があります。 2.3 のループでは 1 度 に 1 行すっ書くと考えたのに対して、こでは 1 度に 1 文字とは限らないということです。 1 度に 1 文字ずつ 書かなければいけない理由などないのです。もちろん、 1 文字でも書けば、仕事は先に進んだことになります。 こで考えるべきことは、出力する文字数を正確に cols にするということだけです。 2.4 ュフレームの文字を書く 最後の仕事は、出力する文字を決めるということです。まず、最初と最後の行上にいるときと、最初と最後の 列上にいるときは、 * を出力することがわかっています。 たとえば、 r が 0 のときは、まだ 1 行も出力していないので、今は最初の行を出力中であることがわかりま 1 なら、これまで rows ー 1 行出力したので、今は最後の行を出力中ということになり す。また、 r が rows ます。同様に、 c が 0 のときは、各行の最初の文字を書くところであり、 c が cols ー 1 なら最後の文字を書く ところです。これにより、コードを書くことができます。 / / 不変な表明 : 現在の行で出力した文字数は c while (c ! = cols) {
108 第 6 章ライブラリのアルゴリズムを使う 以上の知識から、Ⅱ。 t ー url ー char 関数を理解することができます。特に戻り値は、最初に求めた値の真偽を 逆にしていることに注意してください。このため、 not-url-char 関数は、 c がアルファベット、数字、または url-char 内の文字であるとき、 false を戻すのです。そして、 c がこれ以外の文字のとき、 true を戻すのです。 さて、いよいよ url-beg 、つまり難しいところです。この関数は、いろいろな入力を処理する必要があり、 ときには : / / が URL とは無関係な場所でも使われるので、とてもごちやごちゃします。実際には、可能な 、尻。 c 。局田 me がみなわかっていて、それを探す方法も考えられますが、 こでは簡単に考えることにします。 : / / の前には 1 文字以上の文字があり、その後ろには少なくとも 1 文字以上がある場合を、 URL と考えるの string: : const—iterator url—beg(string: : const—iterator b, string: : const—iterator e) -beg; while (beg ! = b & & isalpha(beg[-l] ) ) iter beg = i; / / beg はプロトコル名の最初を指すようにする if (i ! = b & & i + sep . size ( ) ! = e) { / / 「 : / / 」が最初や最後にないことを確認する while ( (i search(), e, sep ・ begin(), sep. end())) ! = e) { it er i = b ; / / i は「 : / / 」が見つかった場所を指すようにする typedef string: : const—iterator iter; static const string sep この関数は b と e で挟まれる文字列に対して働きます。 たときに生成され初期化され、以後はそれを変更することはできません。 この string は static で const です。そのため、この stri Ⅱ g オプジェクトは url ー beg が最初に呼び出され るローカルな string オプジェクトを定義しています。 not_url-char 関数 ( 6.1.3 ) の url_char と同じく、 し URL があるなら、その最初の文字の反復子を戻します。また、 URL を特定するための文字列 : / / を保持す 関数のヘッダ部分 ( パラメータリストと戻り値 ) は簡単です。検索範囲を示すため 2 つの反復子が与えられ、も return e; i + = sep ・ size(); / / 「 : / / 」が URL の一部でない場合は、 i をその分だけ進める return beg; if (beg ! = i & & !not-url-char(i[sep. size()])) / / 「 : / / 」の前後に 1 つ以上の有効な文字があることを確認する
8.2 テータ構造非依存性 149 こまで見てきたアルゴリズムで実際にランダムアクセス反復子を使っているのは sort 関数です。 vector と stri Ⅱ g の反復子はランダムアクセス反復子なのです。しかし、 list の反復子は違います。 list には双方向 反復子しかありません。どうしてでしようか。 list は要素のすばやい挿入と削除を最適化するように作られているからです。そのため、 list の任意の要素 にすばやくアクセスすることはできません。任意の要素にアクセスするには、シーケンスの各要素を最初からた どっていかなければならないのです。 8.2.7 反復子の範囲と最後の 1 つ後の値 これまで見てきたように、アルゴリズムで範囲を指定するのに 2 つの引数を使う方法は、標準ライプラリで非 こで前の引数は範囲の先頭 ( 最初の要素 ) を指し、後の引数は範囲の末尾 ( 最後 常によく見られるものです。 の要素の 1 つ後 ) を指します。どうして、最後の要素の 1 つ後を考えるのでしよう。そもそもそれは有効なの でしようか。 実は 2.6 で、与えられた範囲の上限として最後の要素の 1 つ後が重要であることを見ています。つまり、最 後の要素の値によって範囲の終わりを示すようにしておくと、最後の要素は特別なものになるのです。最後の値 が特別扱いされると、最後に到達する前に誤って終了するループを書きがちになります。ループに関していう と、範囲の最後を示すのに直接最後の要素を指す反復子ではなく、その 1 つ後を指す反復子を使う理由は、さら に 3 つあります。 最初の理由は範囲に要素がまったくない場合です。その場合、最後の要素もないのです。そのような場合、最 後の要素を指す反復子が、最初の要素の 1 つ前を指すようにすることになるでしよう。しかし、そうすると、範 囲に何も要素がない場合だけ特別な処理をするようにしなければなりません。これではプログラムは、わかりに くく信頼できないものになります。毯 6.1.1 で見たように、範囲に要素がなくても要素がある場合と同じように 扱えるということで、プログラムが簡単になるのです。 2 番目の理由は、範囲の最後を示すのに最後の要素の 1 つ後を使うと、引数の反復子同士が等しいか等しくな いかを一度だけ調べればよくなるということです。ポイントは、引数の 2 つの反復子が一致していれば、指定さ れた範囲に要素がないということがすぐわかるということです。そして、指定された範囲に要素がないのはこの 場合に限られています。逆に 2 つの反復子が異なれば、前の反復子は実際の要素を指し、それを処理してループ の次のステップに進み、処理すべき範囲を要素 1 つ分小さくできるわけです。より具体的にいうと、最初の要素 を指す反復子と最後の要素の 1 っ後を指す要素で範囲を指定すると、次のような形のループが書けるということ / / 不変な表明 : [begin, end ) の範囲を処理しなければならない while (begin ! = end) { / / begi Ⅱの指す要素を処理する + + begin; 各ステップで、反復子同士が等しくないか ( または等しいか ) どうかを 1 度調べているだけです。 3 番目の理由は、範囲を示すのに最初の要素の位置と最後の要素の位置の 1 つ後を使うことで、「範囲の外」を 自然に示すことができるということです。自分で書くアルゴリズムもそうですが、多くの標準ライプラリのアル ゴリズムは 2 番目の範囲外の反復子を戻すことでなんらかの失敗を表せます。たとえば、毯 6.1.3 で URL を探
12 * He110 , Estragon! * 第 1 章 string を使う このプログラムは最後に 5 行出力します。その最初の行はフレーム ( 枠 ) の始めですが、これは、 * という文字 十 name 十一リ " ・ const std: :string second = " * " 十 spaces 十 " * " ・ const std: : string spaces(greeting ・ size() , / / 2 行目と 4 行目を作る const std: : string greeting " He110 , / / 後で使うため、あいさつのメッセージを作る std: :cin > > name; std: : string name ; std: :cout くく " あなたの姓を人力してください : int main ( ) #include く string> #include く iostream> / / ユーザに姓を聞き、フレーム付きのあいさつを出力するプログラム と以下のようになります。 もとにあいさつのメッセージを作り、それを後で出力のときに使うのです。そのような方針でプログラムを書く プログラムでは、最初に出力するものを作っておくのが普通でしよう。ユーザに名前を入力させたら、それを 目と同じです。 があり、次にあいさつのメッセージがあり、空白と * で終わります。 4 行目と 5 行目は、それぞれ、 2 行目と 1 行 くなるよう調節してあります。 2 行目は両端に * があり、あとは適当な数の空白です。 3 行目は、始めに * と空白 の列です。その長さは、ユーザの名前に「 He11 。 , 」という文字列と前後の空白と * の長さを足したものに等し / / すべてを出力する const std: : string first (second. size ( ) , / / 1 行目と 5 行目を作る std: :cout std: : cout std: : cout std: : cout std: : cout std: : cout return 0 ; このプログラムは、 std: : endl ; くく first くく std: :endl; second くく std: :endl; くく greeting くく second くく std: :endl ; くく first くく std: :endl; " * " くく st d : : end1 ; くく くく くく くく まずューザに姓を聞き、それを name という変数に格納します。それから、あいさつメッ セージを greeting という変数に入れています。それから、 greet Ⅱ g に格納されている文字の数と同じ数の空 白を spaces という変数に格納しています。そして、出力する 2 行目を作って sec 。Ⅱ d に格納し、 second にあ る文字の数と同じ数の * を含む 1 行目を作って f irst に格納しています。その後は、それらを 1 行ずつ出力して いるのです。 # include ディレクテイプと main の中の最初の 3 行はもう難しくないでしよう。一方、 greeting の定義に は新しいことが 3 つほどあります。 まず、変数には最初から値を与えることができるということです。それには、変数の名前の後に = をつけ、そ の後に、与えたい値を書き、最後に ; を書けばよいのです。変数と値の型が違う場合 ( 10.2 で string と文字
いうものです。そして、このメモリを使い切ったときに限り、新たにより多くメモリを確保するようにします。 簡単のため、 push ー back が余分なメモリを必要とした場合、現在のメモリの倍のメモリを確保するようにしま す。たとえば、最初に vec に要素を 100 個入れて生成したとします。すると push ー back をはじめて呼び出すと 、 200 要素分のメモリを確保するのです。そして最初の 100 個の要素を新しく確保したメモリの前半部にコ ピーし、新しい要素をその後に入れます。こうすると、その後の push ー back の 99 回の呼び出しでは、メモリを 新たに確保する必要はなくなるのです。 この方法では、内部のデータを保持する配列の扱いが変わってきます。もちろん、最初の要素を指すポインタ は依然として必要です。しかし、「最後」を表すポインタは 2 つ必要になるのです。 1 つは、末尾 ( 最後の 1 つ 後 ) の要素を指すポインタです。これは次に使える場所を指すポインタということもできます。もう 1 つのポイ ンタは確保したメモリの末尾 ( 最後の 1 つ後 ) を指すポインタです。 vec オプジェクトは次のような構造になり 202 / / 残りのインタフェースと実装は前と同し 第 11 章抽象データ型を定義する ます。 Vec data avail limit 初期化された要素 初期化されていないメモリ領域 幸い、この新しいメンバを扱うのは、 push ー back 関数とまだ書いていないメモリ管理関数だけです。加えて、 push-back そのものはとても簡単です。 grow ( 成長という意味 ) と unchecked_append ( チェックせずに追加 という意味 ) という新しいメモリ関数を後で書くことにして、 push ー back の仕事の大変なところはそれらに任 せてしまうのです。 template く class T> class Vec { public : void push-back (const T& val) { / / 必要ならメモリを確保 if (avail limit) grow() ; / / 新しい要素を付け加える unchecked—append (val) ; private : iterator data ; iterator avail ; iterator limit ; / / 前と同様に vec の最初の要素へのポインタ / / 保持されている末尾 ( 最後の 1 つ後 ) を指すポインタ / / 確保されているメモリの末尾 ( 最後の 1 つ後 ) を指すポインタ 11.5 柔軟なメモリ管理 vec クラスを書き始めたときに、組み込みの new や delete でメモリ管理ができないことを見ました。これら を使うと、標準の vector に比べて vec に柔軟性がなくなるからです。Ⅱ ew は私たちの目的以上の仕事、つまり メモリ確保と初期化の両方をしてしまうのです。 new を使って型 T の配列を作る場合、 T にはデフォルトコンス
314 c . erase(), e) 付録 B ライブラリのまとめ it で示される要素や範囲 [b , e) で示される要素を c から削除する。削除された要素を指す反復 子はすべて無効になる。 c が vector か string の場合、削除された要素以降の要素を指す反復子 も無効になる。戻り値は、削除された要素の直後を指す反復子。 vector と stri Ⅱ g に対しては、 削除する場所が最後から遠い場合、遅いことに注音 c ・ assign(), e) 入力反復子 b と e の間のシーケンスの要素で c の要素を置き換える。 c . front() c の最初の要素の参照を戻す。 c が空のときの動作は不定。 c . back() c の最後の要素の参照を戻す。 c が空のときの動作は不定。 c . push-back(t) t のコピーを c の最後に付け加える。これにより c のサイズは増える。 void を戻す。 c の最後の要素を削除する。 void を戻す。 c が空のときの動作は不定。 c ・ pop—back() c 内の it で示される直前に値を挿入するための出力反復子を戻す。く iterator> で宣言されて inserter(), it) されている。 back—inserter(c) いる。 c の最後に、 push-back を使って、値を付け加えるための出力反復子を戻す。く iterator> で宣言 ある種の操作は、それが効率的なコンテナでのみ可能になっています。それには次のようなものがあります。 B. 2.3 その他のシーケンシャルな操作 c [ Ⅱ ] c の第 n 番の要素への参照。ただし、最初の要素は第 0 番とされる。 c が c 。 nst のときは、参照 も const になり、 c が const でなければ参照も const でない。Ⅱが範囲外のときの動作は不定。 vector と string に対してのみ有効。 t のコピーを c の先頭に挿入する。これにより c のサイズは増える。 v 。 id を戻す。 string や vector では使えない。 c の最初の要素を削除する。 void を戻す。 c が空のときの動作は不定。 list についてのみ有効。 front—inserter(c) c の先頭に、 c ・ push-front を使って、要素を挿入するための反復子を戻す。く iterator> に定義 されている。 c . push-front (t) c ・ pop-front ( )
176 大抵の言語では、 第 10 章メモリ管理と低レベルのデータ構造 このようなインデックス付けは、基本的で明確です。しかし、 C ト + においては、これは配列 の直接的な性質ではないのです。むしろ、これは、配列の名前とアドレスの関係、また、ポインタがランダムア クセス反復子であるという事実から来るのです。 10.1.6 配列の初期化 配列には標準ライプラリのコンテナにない重要な性質があります。それは、配列の各要素に初期値を与える簡 単な記法があるということです。さらに、この記法には、配列の大きさを明示しなくてもよいという便利さがあ ります。 たとえば、日付を扱うプログラムを書く場合、それぞれの月に日数がどれだけあるかを保持しておく必要があ るかもしれません。これは、たとえば次のようにするとよいのです。 const int month—lengths [ ] / / うるう年以外を考えています 31 , 28 , 31 , 30 , 31 , 30 , 31 , 31 , 30 , 31 , 30 , 31 こで、 1 月が第 0 番要素に対応し、順番に 12 月が第 11 番要素に対応しています。そして、上の書き方で、そ れぞれの要素の値を、その月の日数に初期化できるのです。このような配列を定義しておくと、以後、第 i 番の 月の日数は month-1engthCi] で表すことができるのです。 上の定義では、 m 。Ⅱ th ー le Ⅱ gth の要素数を明示していないことに注意してください。 こでは、要素の初期化 を具体的にしているので、コンパイラが要素数を数えてくれるのです。このような作業はプログラマがするより コンパイラが行う方が間違いが少ないでしよう。 10.2 文字列リテラル再論 こまで来てようやく文字列リテラルの本当の意味を理解できるだけの知識を得ました。文字列リテラルと は、実際には、文字数より 1 つ大きいサイズの c 。Ⅱ st char の配列なのです。文字列に付け足す要素はヌル文 字 ( つまり \ 0 ) で、これはコンパイラが文字列の最後に付け加えるのです。これはつまり、 const char he110 ロ 1 ' 0 のように書くと、 hello が " He11 。 " と同じ意味になるということです。もちろん、 hello という変数そのものは 文字列リテラルとは異なるオプジェクトであり、したがってアドレスも違うことはいうまでもありません。 コンパイラが文字列の最後にヌル文字を入れる理由は、 こうすると、最初の文字のアドレスを渡すだけで文字 列の最後が見つけられるようになるからです。ヌル文字は文字列の最後を示すマークとなり、プログラマにその 事実を使えるのです。たとえば、く cstring> に strlen という関数が定義されています。これは、文字列リテラ ル、つまりヌル文字で終わっている文字の配列の文字数 ( ただしヌル文字は数えない ) を戻す関数です。 strlen の実装には、たとえば次のようなものが考えられます。 / / 標準ライプラリ関数の実装例 size—t strlen(const char* p)
6 ・ 1 string の解析 文章 http b = url—beg(b, 107 ↓ 文章 www. accelerattedcpp. com e) after url—end(), e) この範囲の文字列から string オプジェクトを生成し、それを ret に格納しています。 次の仕事は、 b を進めてまた次の URL を探すだけです。 2 つの URL が重なることはないので、 b を先に見 つけた URL の末尾 ( 最後の文字の 1 つ後 ) にセットし、入力全部を調べ終わるまで while ループを続けます。 ループが終了すると、 URL のすべてが記録されている ret を戻します。 さあ、 url-beg と url-end を考えましよう。 url ー end の方が簡単なので、ますこちらを見てみます。 string: : const—iterator url—end(string: :const—iterator b, string: :const—iterator e) return find—if (), e , not-url-char) ; この関数は単に、 6.1.1 で紹介したライプラリの find ー if に仕事をさせているだけです。 find-if に渡される 判定関数の not-url-char は、次のように自分で書く関数です。これは URL で使えない文字に対して true を 戻す関数です。 b001 not—url—char(char c) / / アルファベット以外で URL に使える文字 static const string url—ch / / c が URL に使えるかどうかをチェックし、その真偽を逆にしたものを戻す return ! (isalnum(c) Ⅱ find(url—ch. begin() , url—ch. end() , c) ! = url-ch. end()) ; この関数のコードは短いものですが、新しいものをいくつか使っています。最初は記憶クラスの規定子 (storage class specifier) である static です。 static 宣言されたローカル変数は、関数の複数回の呼び出しに渡って保 持されます。つまり、 url-char という string オプジェクトは、Ⅱ。 t ー url ー char の最初の呼び出し時に生成さ れ初期化され、以後の呼び出し時にはそれが使われるだけになるのです。 url-ch は c 。 nst であるので、その値 を初期値から変えることもありません。 また、 not-url-char 関数は、く cctype > ヘッダで定義される isal Ⅱ um 関数も使っています。これは、その引 数がアルファベットと数字のどちらかである場合 true を戻す関数です。 最後には、 f ind という新しいアルゴリズムを使っています。これは f ind-if と似ていますが、判定関数では なく特定の値を 3 番目の引数に取り、その値を探すのです。そして、 find-if と同様に、探している値があった 場合、シーケンス内でその値を持っている最初の要素を指す反復子を戻すことになっています。もし検索してい る値がなければ、 fi Ⅱ d は第 2 引数を戻すのです。
2.5 フレームを描くプログラムの完成 while (r ! = rows) { / / r を変えないコード 十十 ; 27 です。このような while 文はよく見かけるものです。まず、 while の前に、 while の条件で調べる変数 r を初 rows ; 十十 r ) { な構造がとても多いので、これを短く書く方法が言語にあるのです。それは、 期化しています。それから、 while の中身では r の値を変え、最後には条件が成り立たなくなります。このよう / / てを変えないコード for (int r = 0 ; r ! = expresston ; s ね m while ( co 〃市〃 0 れ ) { 乞〃ー 0 me れカ より一般的に for 文の意味を書くと次のようになります。 調べられ、中身が実行され、 e 7 ℃ ss 〃が実行され ... が、 c 。〃市。〃が false になるまで繰り返されます。 f 。 r の中身が実行されるのです。そして、その後に e 7 ℃ ss 〃が実行されます。それから、また、 c 。れ市。れが ループの各ステップ ( 最初のステップから ) で c 。れ市。〃は調べられます。 co 〃市 0 れが true のときのみ、 使えません。 * 3 変数を定義し初期化します。 f 。 r ヘッダ内で定義された変数は、 f 。 r 文の終了時に破棄されるので、それ以後は for 文の実行は for ヘッダの s me 襯の実行から始まります。普通はそこで、 co れ市 0 れに使うループ いるからです。 注意してください。というのは、 init-statement はステートメントであり、通常はそれ自身が ; を最後に持って 御するのです。 こで初期ステートメント init-statement と co 〃市 0 〃の間にはわざと ; を書かなかったことに こで最初の行は for ヘッダ (for header) などと言われます。この部分が、次の f 。 r の中身 (for body) を制 statement for ( 乞〃ー s カ佖 me れ co れ市 0 れ ; e 工 7 ℃ ss 〃 ) for 文 (for statement) は一般に次のように書けます。 のことで、 4 は含まれません。同様に r の範囲は [ 0 , rows ) となります。 のようにアンバランスなのは、範囲の表現のため、わざとです。たとえば、 [ 1 , 4 ) と書けば、それは 1 、 2 、 3 範囲は、半分開いた範囲 (half-open range) とよばれ、 [ わ e ツれ , 0 ルル e ー印のと書かれます。カッコが [ が範囲の始まり、 r 。 ws が範囲の終わりの後 (off-the-endvalue) を表わすと考えることができます。このような こで 0 です。どちらの形式を使っても、 r には 0 から rows ー 1 までの間の数が順番に入れられていきます。 * 3 訳注 : まだこの仕様にしたがっていないコンパイラも見受けられます。
5.9 詳細 95 気を付けるところは 1 ヶ所だけで、それは、片方の絵の方がもう 1 つの絵より先にコピーが終わってしまう場 合の処理です。上の関数は繰り返しを、入力した絵 (vector) のすべてをコピーするまで続けています。つま り while ループは、両方の絵の最後に到達するまで繰り返しを続けるのです。 まだ left の絵をすべてコピーしないうちは、その 1 行を s にコピーしますが、 left にコピーするべき行が 残っていてもいなくても、必要な数の空白だけ string オプジェクトを作って、 s に + = でつなぎます。この演算 子は、 string オプジェクトに対して、たぶん普通の人が予想するように、右のオペランドを左のオペランドに こで「足す」ということは、「つなぐ」ことな つなぎ、その結果を左のオペランドに格納するのです。つまり、 のです。 詰め込む空白の数は widthl から s . size() 引いて出しています。 s . size() は左の絵からコピーした行の長 こで 0 になるのは、左の絵がコピーされつくされ、もう何もコピーされなかった場 さか、 0 になるはすです。 合です。 width 1 は 2 つの絵の間に空白を 1 つ入れる目的で左の絵の最長の行の長さに 1 を足したものになって います。そのため、 s . size ( ) は、 s に左の絵をコピーした場合でも、 widthl より大きくなることはありませ ん。この場合は、 1 つ以上の空白が左の絵の行の右に詰め込まれることになります。一方、 s . size() が 0 なら、 長さ widthl の空白を s にコピーすることになります。 左の絵に空白を詰め込んだものを s にコピーし、さらに右の絵が残っているなら、次は右の絵の行をコピーす るだけです。ただし、もう右の絵が残っていないなら、 s には何も付け加えません。いすれにしても、この s を 出力する vector に追加します。こうして、左右の入力の絵のすべてをコピーし終わったら、新しい絵である ret を戻して関数は終了です。 こで、 s が while ループ内で定義されていることに注意してください。 s はループ内で繰り返しのたびに空 の文字列として生成され、使用後は破棄されているのです。そのようになっていなければ、このコードは正しく 働かないのです。 5.9 詳細 標準ライプラリでは、異なるコンテナに対する似たような操作は、イン コンテナと反復子 ( イテレータ ) : こまで扱ってきたコンテナはみなシーケンシャル タフェースと構文が同じになるように工夫されている。 (sequential) である。第 7 章では、標準ライプラリの連想コンテナを紹介する。すべてのシーケンシャルなコン テナと string には次のような操作がある。 container く T> : : iterator container く T>: :const_iterator コンテナ container く T> の反復子の型名。 container く T> : : size—type c . rend() コンテナを逆順に扱うための、最後と最初 ( の 1 つ前 ) の要素を指す反復子。 c . rbegin() c . end() コンテナの先頭と末尾 ( 最後の要素の 1 つ後 ) の要素を指す反復子。 c ・ begin() コンテナ c 。Ⅱ tainer く T > の最大サイズを扱える適当な型の名前。