🎯
コンテナ中の要素の有無を調べる
std::vector
、std::set
、std::map
のようなコンテナ型にある要素が入っているかをチェックしたい時は、どういうコードを使いますか?
一番王道でC++っぽいアプローチはやはりfor
ループで、実際に未だに一番よく見かけます。
範囲ベースの for
ループを使えば、以下のようになります。
#include <string>
#include <vector>
bool contains1(const std::vector<std::string>& names, const std::string& to_find)
{
for (const auto& name : names)
{
if (name == to_find)
{
return true;
}
}
return false;
}
見つけた際にインデックスが欲しい場合は、普通のfor
ループを使わないといけないです。
#include <string>
#include <vector>
int find1(const std::vector<std::string>& names, const std::string& to_find)
{
for (int i = 0; i < names.size(); ++i)
{
if (names.at(i) == to_find)
{
return i;
}
}
return -1;
}
でも毎回自分でfor
ループを書くのもあれなので、標準ライブラリのalgorithm
のヘッダーにあるstd::find
という関数を使うという手もあります。
#include <algorithm>
#include <iterator>
#include <string>
#include <vector>
bool contains2(const std::vector<std::string>& names, const std::string& to_find)
{
return std::find(names.begin(), names.end(), to_find) != names.end();
}
std::find
は見つけた場合にイテレーターを返すため、インデックスが欲しい場合は、例えば以下のようにstd::distance
という関数を使って、最初のイテレーターからの距離を計算することで取得できます。
#include <algorithm>
#include <iterator>
#include <string>
#include <vector>
int find2(const std::vector<std::string>& names, const std::string& to_find)
{
const auto it = std::find(names.begin(), names.end(), to_find);
if (it != names.end())
{
return std::distance(names.begin(), it);
}
else
{
return -1;
}
}
C++20 だったら、ranges
ライブラリを使えば、以下のようにもう少し簡単に書けます。
#include <algorithm>
#include <iterator>
#include <string>
#include <vector>
bool contains2New(const std::vector<std::string>& names, const std::string& to_find)
{
return std::ranges::find(names, to_find) != names.end();
}
#include <algorithm>
#include <iterator>
#include <string>
#include <vector>
int find2New(const std::vector<std::string>& names, const std::string& to_find)
{
const auto it = std::ranges::find(names, to_find);
if (it != names.end())
{
return std::distance(names.begin(), it);
}
else
{
return -1;
}
}
後、これはたまに見かける方法ですが、std::count
という要素の数を数えてくれる関数を使えば、以下のようにもっと簡単に書けます。
#include <algorithm>
#include <string>
#include <vector>
bool contains3(const std::vector<std::string>& names, const std::string& to_find)
{
return std::count(names.begin(), names.end(), to_find);
}
数が0
だったらfalse
に、1
だったらtrue
に暗黙キャストされます。でもこの方法ではインデックスが取得できないです。
こんな簡単な問題でもこんなにも違う方法があるのが C++ の強みでもあり、苦戦するところでもあります。普通のプログラミング言語だったら、各コンテナにContains()
のような特化した関数があって終わりなはずのに、C++ だと大体自分で書かないといけないです。
因みにですが、インデックスがいらない場合は、C++23 からはようやくstd::ranges::contains
という関数が追加されます。
#include <algorithm>
#include <iterator>
#include <string>
#include <vector>
bool contains4(const std::vector<std::string>& names, const std::string& to_find)
{
return std::ranges::contains(names, to_find);
}
Discussion