Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use std::any_of?

I want to compare one value against several others and check if it matches at least one of those values, I assumed it would be something like

if (x = any_of(1, 2 ,3)
   // do something

But the examples of it I've seen online have been

bool any_of(InputIt first, InputIt last, UnaryPredicate)

What does that mean?

like image 773
baguettio Avatar asked Dec 29 '25 12:12

baguettio


2 Answers

There is plenty of literature and video tutorials on the subject of "iterators in C++", you should do some research in that direction because it's a fundamental concept in C++.

A quick summary on the matter: an iterator is something that points to an element in a collection (or range) of values. A few examples of such collections:

  • std::vector is the most common one. It's basically a resizable array.
  • std::list is a linked list.
  • std::array is a fixed size array with some nice helpers around C style arrays
  • int myInt[12] is a C style array of integers. This one shouldn't be used anymore.

Algorithms from the C++ standard library that operate on a collection of values (such as std::any_of) take the collection by two iterators. The first iterator InputIt first points to the beginning of said collection, while InputIt last points to the end of the collection (actually one past the end).

A UnaryPredicate is a function that takes 1 argument (unary) and returns a bool (predicate).

In order to make std::any_of do what you want, you have to put your values in a collection and x in the UnaryPredicate:

int x = 3;
std::vector values = {1, 2, 3};
if (std::any_of(values.begin(), values.end(), [x](int y) { return x == y; }))
    // ...

The UnaryPredicate in this case is a lambda function.

As you can see this is quite verbose code given your example. But once you have a dynamic amound of values that you want to compare, or you want to check for more complex things than just equality, this algorithm becomes way more beneficial.

Fun little experiment

Just for fun, I made a little code snippet that implements an any_of like you wanted to have it. It's quite a lot of code and pretty complicated aswell (definitely not beginner level!) but it is very flexible and actually nice to use. The full code can be found here.

Here is how you would use it:

int main()
{
    int x = 7;
    std::vector dynamic_int_range = {1, 2, 3, 4, 5, 6, 7, 8};

    if (x == any_of(1, 2, 3, 4, 5))
    {
        std::cout << "x is in the compile time collection!\n";
    }
    else if (x == any_of(dynamic_int_range))
    {
        std::cout << "x is in the run time collection!\n";
    }
    else
    {
        std::cout << "x is not in the collection :(\n";
    }   


    std::string s = "abc";
    std::vector<std::string> dynamic_string_range = {"xyz", "uvw", "rst", "opq"};

    if (s == any_of("abc", "def", "ghi"))
    {
        std::cout << "s is in the compile time collection!\n";
    }
    else if (s == any_of(dynamic_string_range))
    {
        std::cout << "s is in the run time collection!\n";
    }
    else
    {
        std::cout << "s is not in the collection :(\n";
    }    
}

And here how it's implemented:

namespace detail
{
    template <typename ...Args>
    struct ct_any_of_helper
    {
        std::tuple<Args...> values;

        constexpr ct_any_of_helper(Args... values) : values(std::move(values)...) { }

        template <typename T>
        [[nodiscard]] friend constexpr bool operator==(T lhs, ct_any_of_helper const& rhs) noexcept
        {        
            return std::apply([&](auto... vals) { return ((lhs == vals) || ...); }, rhs.values);
        }
    };

    template <typename Container>
    struct rt_any_of_helper
    {
        Container const& values;

        constexpr rt_any_of_helper(Container const& values) : values(values) { }

        template <typename T>
        [[nodiscard]] friend constexpr bool operator==(T&& lhs, rt_any_of_helper&& rhs) noexcept
        {
            return std::any_of(cbegin(rhs.values), cend(rhs.values), [&](auto val)
            {
                return lhs == val;
            });
        }
    };

    template <typename T>
    auto is_container(int) -> decltype(cbegin(std::declval<T>()) == cend(std::declval<T>()), std::true_type{});

    template <typename T>
    std::false_type is_container(...);

    template <typename T>
    constexpr bool is_container_v = decltype(is_container<T>(0))::value;
}

template <typename ...Args>
[[nodiscard]] constexpr auto any_of(Args&&... values)
{
    using namespace detail;

    if constexpr (sizeof...(Args) == 1 && is_container_v<std::tuple_element_t<0, std::tuple<Args...>>>)
        return rt_any_of_helper(std::forward<Args>(values)...);
    else
        return ct_any_of_helper(std::forward<Args>(values)...);
}

In case an expert sees this code and wants to complain about the dangling reference: come on, who would write someting like this:

auto a = any_of(std::array {1, 2, 3, 4});
if (x == std::move(a)) // ...
like image 198
Timo Avatar answered Dec 31 '25 04:12

Timo


That's not what this function is for.

Your values must already exist somewhere else, it is very likely that it will be a vector.

std::any_of operates on iterators.

Iterators in C++ are ranges, two values that tell you where is the beginning, and where is the end of the range.

Most C++ Standard Template Library collections, including std::vector, support iterator API, and so you can use std::any_of on them.

For the sake of a full example, lets check if a vector contains 42 in over the top way, just to use std::any_of.

Since we only want to check if value in vector exists without changing anything (std::any_of doesn't modify the collection), we use .cbegin() and .cend() that return constant beginning and end of the vector, those are important to std::any_of, as it has to iterate over the entire vector to check if there's at least one value matching the given predicate.

The last parameter must be unary predicate, that means that it is a function, that accepts a single argument, and returns whether given argument fits some criteria.

To put it simply, std::any_of is used to check whether there's at least one value in a collection, that has some property that you care about.

Code:

#include <algorithm>
#include <iostream>
#include <vector>

bool is_42(int value) {
    return value == 42;
}

int main() {
    std::vector<int> vec{
        1, 2, 3,
        // 42 // uncomment this
    };

    if(std::any_of(vec.cbegin(), vec.cend(), is_42)) {
        std::cout << "42 is in vec" << std::endl;
    } else {
        std::cout << "42 isn't in vec" << std::endl;
    }
}

Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!