vector<string>型変数を、変数名[][]という2次元配列として扱える件について

## 初めに
C++におけるvector\<string>型変数の柔軟な扱い方について学習した記事です。
結論から言うと、vector\<string>型変数:hogeはhoge[i][j]というように2次元配列として扱えます。
hoge[i]に収められている文字列を、hoge[i][j]で1文字ずつchar型として取り出せます。
**ご指摘あればコメントでお願いいたします。**

##### 2023/12/23追記
コメントで今回扱ったような、「配列の配列」を**ジャグ配列**と呼ぶことをご教示いただきました。
明解な概念を教えてくださり感謝いたします！
>char Array[3][5] ← 「２次元配列」というとこういうやつで，
>string Strs[3] みたいなのは，各stringを「charの配列」と考えるならば「配列の配列」なのであり，そういうのを「ジャグ配列 (jagged array)」と呼ぶ様子．
>（後者の側は，個々の配列：ここではstring のサイズが異なり得る）

以下はC#でのジャグ配列についての具体例。↓
>int[][] jarray = new int[2][];
>jarray[0] = new int[] { 1, 2 };
>jarray[1] = new int[] { 3, 4, 5 };

https://atmarkit.itmedia.co.jp/ait/articles/1701/10/news021.html

##### 追記ここまで

## きっかけ
C++で競技プログラミングサイトAtCoderの過去問
「ABC323　B - Round-Robin Tournament」
のユーザ解説を見ていて引っかかる点があった。

https://atcoder.jp/contests/abc323/editorial/7366

18行目のこの部分。

>**c[i][j]**

この変数cは、9行目に宣言されている通りvector\<string>型のvector。
さらっと流しそうになったのだが、2次元配列として扱っていることに躓く。

### なぜ躓いたか
そもそも1次元ベクトルとして宣言しているので、c[i]としてアクセスするのは理解できる。
しかしどう見てもc[i][j]というのは2次元配列であり、しかもエラーにならず想定通りの挙動をしている。
想定というのはこの場合、i行目のj番目の文字列、-oxのうちのどれかにアクセスできているということ。
なぜこのような挙動ができるのか？素直に理解できず躓いてしまった。
ネットで調査してみても、明解なものが得られず。
(あくまで今回の疑問についてです。このような有用な知識を残してくださった方々に多謝)
「C++ vector string」などで検索。

>C++のvectorまとめ
>https://qiita.com/ysuzuki19/items/df872d91c9c89cc31aee

>C++ で文字列の配列を作成する
>https://www.delftstack.com/ja/howto/cpp/string-array-in-cpp/

>競プロ等におけるC++の標準入力
>https://qiita.com/zenrshon/items/cd19361842ae887afe8a

>【C++】2次元配列の入力＆出力方法
>https://note.com/imsakasa/n/n0fcf0fd88744

>std::vecotrを２次元配列として扱う
>https://kaworu.jpn.org/cpp/std::vecotr%E3%82%92%EF%BC%92%E6%AC%A1%E5%85%83%E9%85%8D%E5%88%97%E3%81%A8%E3%81%97%E3%81%A6%E6%89%B1%E3%81%86

## ChatGPTに頼る
結局、ChatGPT(3.5)に質問することに。ただ誤答もありえるため、裏付けをとるように心がける。
質問してみたところ、以下回答が得られた。
>C++のstd::vectorやstd::stringなどの標準ライブラリコンテナは、operator[]をオーバーロードして、要素にアクセスするためのインターフェースを提供しています。
>例えば、vector型の変数vがある場合、v[i]でi番目の要素にアクセスできます。これは、内部的にoperator[]がオーバーロードされており、対応する要素への参照（またはポインタ）を返すようになっています。
>同様に、string型もoperator[]をオーバーロードしており、文字列中の各文字へアクセスできます。string型は実際にはchar型の配列として実装されているため、「abc」という文字列では0番目が'a'、1番目が'b'というようなアクセスが可能です。
>この仕組みを利用することで、「c[i]」と書くことでi番目の文字列（c内部ではstring型）へアクセスし、「c[i][j]」と書くことでi番目の文字列中でj番目の文字（char型）へアクセスすることができます。
>つまり、「c[i][j]」は「c[i]」から返されるstring型オブジェクトに対してさらに「[j]」を適用したものです。最初に「c[i]」が呼ばれて該当するstringオブジェクトへアクセスし、「[j]」が呼ばれてその中から該当するchar型要素へアクセスします。
>したがって、二次元的な配列やベクター操作を必要とする場合でも簡単かつ直感的な記述方法です。

 

それらしい回答が得られた。個人的に重要だった部分を抜き出すと、

* **\[](インデックス演算子)をオーバーロードし**、vectorでも利用できるようにしている
* vector<string>と宣言した場合、c[i]でそれぞれのstring型変数にアクセス
* c[i][j]で、**指定したstring型の文字列を1文字ずつchar型として扱い**、文字ごとにアクセス可能

ということだった。
そのため、c[i][j]とすることで文字列を2次元配列のようにアクセスすることが可能、ということになる。

裏付けも探してみたが、こちらの記事が参考になった。

>i番目文字の参照（charに変換）、代入
>[] 演算子（operator[](int)） を使って、通常の文字列と同じように、i 番目の文字の参照（char に変換）・代入が可能。
>operator[](int) と聞くと身構える人がいるかもしれないが、「str[10]」の様に、普通の文字列の要素にアクセスする時と同じ記述だ。 恐れることは何も無い。
>下記は、i 番目の文字の参照（char に変換）と代入例。普通の参照（char に変換）・代入とまったく同じでしょ。
```test.cpp
   std::string str("31415926535");

    for (int i = 0; i < 10; ++i)

        std::cout << str[i] << " ";      //  str の i 番目の要素を表示

    const int SZ = 10;          // 文字数

    std::string str(SZ, 'a');     // 10 文字の 'a' から成る str を生成

    for(int i = 0; i < SZ; ++i)

        str[i] = 'b' + i;             // 文字を 'b', 'c' ... に設定

    std::cout << str << "\n";
```

>http://s170199.ppp.asahi-net.or.jp/tech/cpp/string.html

vectorとして扱った記事ではないが、string型変数に配列としてアクセス→char型変数として取り出せることについて記述されていた。

## 結論
vectorはインデックス演算子[]がオーバーロードされているため、vector[]とすれば各行の要素にアクセスできる。
また、string型変数に[]を付与すれば、char型として1文字ずつアクセスできる。
これらを合わせてvector\<string>型として宣言された変数は、変数名[][]というように2次元配列として扱うことができる。

## 振り返り
できれば公式ドキュメントを参照するのが一番良いのだろうが、そこまで理解できる読解力を持っていなかった。。
強いエンジニアになりたいと願った一件だった。
また、内容が充実している初心者・初級者向けC++の教本を読んだ方がいいかもしれないと痛感した。