🎯

コンテナ中の要素の有無を調べる

に公開

std::vectorstd::setstd::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);
}

|cpp記事一覧へのリンク|

Discussion