今回は、C++でよく出てくる「std::listとは何か」について解説していきます。
「std::listって何?」
「std::vectorとどう違うの?」
「どんな場面で使えばいいの?」
こんな疑問はありませんか?
std::listは、C++の標準ライブラリに入っているコンテナのひとつです。配列のように要素をまとめて扱えますが、性質はかなり違います。この記事では、std::listの特徴・基本的な使い方・初心者がハマりやすいポイントを、できるだけわかりやすく整理していきます。
std::listとは?
std::listは、要素を順番に管理できるコンテナです。特に、途中への追加や削除がしやすいのが特徴です。
std::vectorは要素が連続した領域に並ぶのに対して、std::listは要素同士をつないで管理するタイプのコンテナです。そのため、途中に新しい要素を入れたり、特定の要素を消したりする処理が得意です。
ただし、その代わりに添字で直接アクセスするのは苦手です。たとえば list[0] のような書き方はできません。
なぜstd::listが必要?
配列やstd::vectorだけでも多くの処理は書けますが、要素の追加や削除が頻繁に起きる場面では、std::listのほうが考えやすいことがあります。
たとえば、敵の一覧、タスクの順番、イベント待ちのデータなど、「途中で消える」「途中に追加される」ことが多いデータでは使いやすいです。ループ処理の基本にまだ不安がある方は、for文とwhile文の違いの記事もあわせて読むと理解しやすいです。
std::listの基本的な使い方
まずは、要素の追加・表示・削除の基本を見てみます。
▼main.cpp
#include <iostream>
#include <list>
int main() {
std::list<int> numbers;
numbers.push_back(10);
numbers.push_back(20);
numbers.push_back(30);
std::cout << "追加後: ";
for (int value : numbers) {
std::cout << value << ' ';
}
std::cout << '\n';
numbers.pop_front();
std::cout << "先頭削除後: ";
for (int value : numbers) {
std::cout << value << ' ';
}
std::cout << '\n';
return 0;
}このコードでは、push_backで末尾に要素を追加し、pop_frontで先頭の要素を削除しています。std::listはこのような前後の追加・削除が書きやすいです。
実行結果
追加後: 10 20 30
先頭削除後: 20 30
std::vectorとの違い
初心者のうちは、std::vectorとstd::listの違いで迷いやすいです。ざっくり言うと、添字で扱いやすいのがstd::vector、途中の追加や削除を考えやすいのがstd::listです。
- std::vector:添字アクセスしやすい、扱う場面が多い
- std::list:途中のinsertやeraseがしやすい
- std::list:ランダムアクセスには向かない
そのため、何となくlistを選ぶというより、「途中削除をたくさんしたいかどうか」で判断するのがわかりやすいです。
実践でよく使うinsertとerase
std::listでは、iteratorを使って途中に要素を入れたり、要素を消したりできます。クラスでデータを管理したい場合は、classの記事とあわせて考えると整理しやすいです。
▼main.cpp
#include <iostream>
#include <list>
int main() {
std::list<int> numbers = {10, 20, 30};
auto it = numbers.begin();
++it;
numbers.insert(it, 15);
for (auto iter = numbers.begin(); iter != numbers.end(); ) {
if (*iter == 20) {
iter = numbers.erase(iter);
} else {
++iter;
}
}
std::cout << "結果: ";
for (int value : numbers) {
std::cout << value << ' ';
}
std::cout << '\n';
return 0;
}この例では、20の前に15を追加し、その後20だけ削除しています。eraseの戻り値を次のiteratorとして受け取っているのがポイントです。
実行結果
結果: 10 15 30
【重要】私が実際にstd::listで困った体験談
私が個人開発で敵データの管理をしていたとき、削除が多いからという理由だけで最初からstd::listを選んだことがありました。たしかに要素を消す処理は書きやすかったのですが、あとから「何番目の敵か」を基準に処理を書きたくなって困りました。
std::vectorの感覚で添字アクセスしたくなるのですが、std::listはそこが得意ではありません。その結果、ループのたびにiteratorで進める必要があり、思ったよりコードが見づらくなりました。
最終的には、削除頻度が高い部分だけstd::list、添字で扱いたい部分はstd::vectorのように使い分けるようにしました。個人開発では「何となく便利そう」で選びがちですが、コンテナは用途で分けたほうがかなり管理しやすいです。
std::list使用時のよくある失敗例と対処法
1. 添字でアクセスしようとする
std::listでは list[0] のような書き方はできません。順番にたどる必要があるので、range-based forやiteratorを使いましょう。
2. eraseしたあとにiteratorをそのまま使う
eraseした要素のiteratorをそのまま使うと不具合の原因になります。eraseの戻り値を次のiteratorとして受け取るのが基本です。
3. vectorの代わりに何でもlistにしてしまう
途中削除が少ないなら、std::vectorのほうがシンプルなことも多いです。listは万能ではないので、用途を見て選ぶのが大事です。挙動がわかりにくいときは、Visual Studioのデバッグ記事も参考になります。
注意点
std::listは便利ですが、初心者向けとしてはまずstd::vectorのほうが扱いやすい場面も多いです。特に、番号でアクセスしたい、要素をまとめて見たい、という場合はvectorのほうが自然です。
そのうえで、途中への追加や削除が多い処理で困ったときにstd::listを選ぶ、という考え方にすると失敗しにくいです。
まとめ
- std::listは途中の追加や削除がしやすいコンテナ
- push_back、push_front、insert、eraseをよく使う
- 添字アクセスはできないのでiteratorで扱う
- vectorの代わりに何でもlistにするのはおすすめしにくい
- 用途に応じてvectorとlistを使い分けるのが大事


