C++ count, count_if, all_of, any_of and none_of: Counting and Checking Conditions
Key takeaways
Count matching values and predicates with std::count and count_if; learn all_of, any_of, none_of, empty ranges, and short-circuit behavior in C++.
Introduction
Counting algorithms count elements or test predicates over a range: count, count_if, all_of, any_of, and none_of.
It’s tempting to reach for a hand-written loop for these — “just count with a for loop” feels simpler than remembering five algorithm names — but the standard algorithms earn their keep in three concrete ways. First, all_of/any_of/none_of short-circuit: they stop scanning the moment the answer is determined, which a naive loop that always runs to completion (common when someone writes for (x : v) if (cond) matches++; and checks matches > 0 afterward) doesn’t do for free. Second, the algorithm names document intent directly — all_of(v, isPositive) tells a reviewer what’s being checked without them tracing through loop body logic. Third, because these operate on iterator ranges rather than specific containers, the exact same call works unchanged on a std::vector, a std::list, a raw array via pointers, or a subrange of any of them — a hand-rolled loop tied to .size() and operator[] doesn’t generalize the same way.
count
Basic use
#include <algorithm>
#include <vector>
#include <iostream>
int main() {
std::vector<int> v = {1, 2, 3, 2, 4, 2, 5};
int count = std::count(v.begin(), v.end(), 2);
std::cout << "Count of 2: " << count << std::endl;
return 0;
}
std::count compares each element to the given value with operator== and returns iterator_traits<It>::difference_type — in practice ptrdiff_t, a signed type, not size_t. That signedness is easy to overlook and matters the moment you mix the result with unsigned arithmetic elsewhere (subtracting it from a size_t container size, for instance), where the usual arithmetic conversions silently promote the signed result to unsigned and a logically negative intermediate result wraps around to a huge positive number instead of erroring.
On strings
#include <algorithm>
#include <string>
#include <iostream>
int main() {
std::string text = "hello world";
int count = std::count(text.begin(), text.end(), 'l');
std::cout << "Count of 'l': " << count << std::endl;
return 0;
}
Note that std::string iterators are just char iterators, so counting 'l' in "hello world" is really the same std::count overload as counting 2 in a vector<int> — the algorithm has no special-cased knowledge of strings, which is exactly the point of writing it against iterators rather than concrete container types.
count_if
Conditional counting
count_if generalizes count from equality against a fixed value to an arbitrary predicate, which is what lets it express conditions count structurally cannot, like “even” or “greater than five.” The trade-off for that flexibility is that count_if always evaluates the predicate once per element and cannot short-circuit even when only a boolean answer is ultimately needed (as in the evenCount > 0 check some callers write) — if you only care whether any element matches, any_of is both clearer in intent and faster on average, since it stops at the first hit instead of scanning the whole range.
#include <algorithm>
#include <vector>
#include <iostream>
int main() {
std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int evenCount = std::count_if(v.begin(), v.end(), [](int x) {
return x % 2 == 0;
});
int greaterThan5 = std::count_if(v.begin(), v.end(), [](int x) {
return x > 5;
});
return 0;
}
With structs
Passing a lambda that captures nothing but reads a member field (s.score >= 90) is the common case for count_if in real code — it’s how you count “how many records satisfy a business condition” without writing a bespoke loop and a named comparator function for every one-off query. Because the lambda takes const Student&, this also avoids copying every Student in the range just to inspect one field, which matters once the struct holds anything larger than a couple of ints.
#include <algorithm>
#include <vector>
#include <string>
#include <iostream>
struct Student {
std::string name;
int score;
};
int main() {
std::vector<Student> students = {
{"Alice", 85},
{"Bob", 92},
{"Charlie", 78},
{"David", 95},
{"Eve", 88}
};
int highScores = std::count_if(students.begin(), students.end(),
[](const Student& s) {
return s.score >= 90;
});
return 0;
}
all_of, any_of, none_of
These three are the boolean counterparts to count_if, and the distinction that matters most in practice is short-circuiting: unlike count_if, which always visits every element, all_of stops as soon as it finds a counterexample, any_of stops as soon as it finds a match, and none_of stops as soon as it finds a match (returning false at that point). For an early mismatch in a large range, this can be an order of magnitude faster than a count_if(...) > 0 equivalent — and just as importantly, it reads as a direct boolean question rather than requiring the reader to infer intent from a numeric comparison.
all_of
#include <algorithm>
#include <vector>
#include <iostream>
int main() {
std::vector<int> v1 = {2, 4, 6, 8, 10};
std::vector<int> v2 = {2, 4, 5, 8, 10};
bool allEven1 = std::all_of(v1.begin(), v1.end(), [](int x) {
return x % 2 == 0;
});
bool allEven2 = std::all_of(v2.begin(), v2.end(), [](int x) {
return x % 2 == 0;
});
return 0;
}
all_of on v1 (all even) returns true; on v2, which contains one odd number (5), it returns false — and note it does so only after checking every element up through the 5, since all_of can only conclude “true” by exhausting the range but can conclude “false” the moment it finds a single counterexample. That asymmetry — cheap to disprove, expensive to prove — is inherent to universal quantification and isn’t specific to this implementation; it’s the same reason mathematical proofs of “for all x” are generally harder than a single counterexample that disproves the claim.
any_of
#include <algorithm>
#include <vector>
#include <iostream>
int main() {
std::vector<int> v1 = {1, 3, 5, 7, 9};
std::vector<int> v2 = {1, 3, 4, 7, 9};
bool hasEven1 = std::any_of(v1.begin(), v1.end(), [](int x) {
return x % 2 == 0;
});
bool hasEven2 = std::any_of(v2.begin(), v2.end(), [](int x) {
return x % 2 == 0;
});
return 0;
}
any_of is the mirror image of all_of: cheap to prove (stops at the first match), expensive to disprove (must scan the whole range to conclude “no matches exist”). hasEven1 on v1 (all odd) has to check every element before returning false; hasEven2 on v2 returns true as soon as it reaches 4, without looking at 7 or 9 at all.
none_of
#include <algorithm>
#include <vector>
#include <iostream>
int main() {
std::vector<int> v = {1, 3, 5, 7, 9};
bool noNegative = std::none_of(v.begin(), v.end(), [](int x) {
return x < 0;
});
return 0;
}
none_of(v, pred) is exactly equivalent to !any_of(v, pred), and choosing between them is purely a matter of which reads more naturally at the call site — none_of(v, isNegative) (“no negative numbers”) is clearer than !any_of(v, isNegative) (“not any negative numbers”), even though they compile to the same short-circuiting scan.
Common pitfalls
Empty range
The results for an empty range aren’t arbitrary — they follow from the mathematical definition of quantifiers over an empty set, and getting them backwards is a real source of bugs in validation code. count on an empty range is trivially 0. all_of on an empty range is vacuously true: “every element satisfies the predicate” is true when there are no elements to violate it, the same logical convention used throughout mathematics (an empty product is 1, an empty sum is 0, an empty conjunction is true). any_of on an empty range is false — there’s nothing for the predicate to match. none_of is true for the same vacuous reason as all_of. The practical trap: if you write all_of(items, meetsRequirement) to validate a list before processing, an accidentally-empty items passes validation by definition, which is correct set theory but often not what a caller wants — you usually need an explicit !items.empty() && all_of(...) guard if “no items” should count as invalid.
#include <algorithm>
#include <vector>
#include <iostream>
int main() {
std::vector<int> empty;
int count = std::count(empty.begin(), empty.end(), 5);
bool all = std::all_of(empty.begin(), empty.end(), [](int x) { return x > 0; });
bool any = std::any_of(empty.begin(), empty.end(), [](int x) { return x > 0; });
bool none = std::none_of(empty.begin(), empty.end(), [](int x) { return x < 0; });
return 0;
}
Return type
Assigning the result to auto deduces std::vector<int>::difference_type, which is a signed integer type (typically ptrdiff_t), while assigning to size_t forces a conversion to unsigned. For a count that’s mathematically guaranteed non-negative, the unsigned type feels “more correct,” but it reintroduces the classic C++ trap of mixing signed and unsigned integers if that count is later compared against or subtracted from a signed loop index or another signed count — the compiler will silently perform the usual arithmetic conversions rather than erroring, and a negative-looking result wraps around to a very large positive number instead. When in doubt, keep counts in their natural signed difference_type and only convert to size_t at the point where you actually need to index a container with it.
#include <algorithm>
#include <vector>
#include <iostream>
int main() {
std::vector<int> v = {1, 2, 3, 4, 5};
auto count = std::count(v.begin(), v.end(), 3);
size_t count2 = std::count(v.begin(), v.end(), 3);
return 0;
}
Short-circuit evaluation
This example makes short-circuiting observable rather than just theoretical: adding a side effect (++checks, a print) inside the predicate and inspecting checks afterward shows exactly how many elements any_of actually visited before stopping. Run it and checks will be smaller than v.size() whenever an even number appears before the end — concrete proof that any_of isn’t scanning the whole vector “just in case.” This same technique (a mutable capture that counts predicate invocations) is a useful debugging trick any time you need to confirm whether an algorithm is really short-circuiting in your specific code path, rather than trusting the documentation alone.
#include <algorithm>
#include <vector>
#include <iostream>
int main() {
std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int checks = 0;
bool hasEven = std::any_of(v.begin(), v.end(), [&checks](int x) {
++checks;
std::cout << "check: " << x << std::endl;
return x % 2 == 0;
});
return 0;
}
Performance: multiple passes
Three separate count_if calls, each scanning the full vector, is three full O(n) passes over the data even though the single hand-written loop below computes all three counts in one pass. For a vector that fits comfortably in cache this rarely matters, but it stops being negligible once v is large enough that the difference between one cache-friendly linear scan and three separate ones is measurable — three passes over a multi-gigabyte dataset is three times the memory bandwidth, not three times the CPU work, and memory bandwidth is very often the actual bottleneck for simple per-element predicates. When you find yourself calling multiple count_ifs over the same range for related conditions, folding them into a single loop (or a single std::accumulate with a struct accumulator) is a legitimate, measurable optimization — trading the readability of separate named algorithm calls for one merged pass, a trade worth making once profiling actually shows the repeated scans matter.
#include <algorithm>
#include <vector>
#include <iostream>
int main() {
std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int even = std::count_if(v.begin(), v.end(), [](int x) { return x % 2 == 0; });
int odd = std::count_if(v.begin(), v.end(), [](int x) { return x % 2 != 0; });
int gt5 = std::count_if(v.begin(), v.end(), [](int x) { return x > 5; });
int evenCount = 0, oddCount = 0, gt5Count = 0;
for (int x : v) {
if (x % 2 == 0) ++evenCount;
else ++oddCount;
if (x > 5) ++gt5Count;
}
return 0;
}
Example: statistics utility
This class combines every algorithm covered above into something closer to real usage: analyze mixes accumulate, minmax_element, and three separate count_if/count calls to build a summary report, while validate and inRange use the boolean predicates for input validation. Notice that validate deliberately doesn’t guard against an empty data before calling all_of/none_of — per the vacuous-truth rule covered above, an empty dataset would report allPositive: true and noZero: true, which may or may not be the right behavior depending on whether “no data” should count as valid input for your specific use case.
#include <algorithm>
#include <vector>
#include <numeric>
#include <cmath>
#include <iostream>
class Statistics {
public:
static void analyze(const std::vector<int>& data) {
if (data.empty()) {
std::cout << "No data" << std::endl;
return;
}
size_t count = data.size();
int sum = std::accumulate(data.begin(), data.end(), 0);
double mean = static_cast<double>(sum) / count;
auto [minIt, maxIt] = std::minmax_element(data.begin(), data.end());
int evenCount = std::count_if(data.begin(), data.end(), [](int x) {
return x % 2 == 0;
});
int oddCount = count - evenCount;
int positive = std::count_if(data.begin(), data.end(), [](int x) { return x > 0; });
int negative = std::count_if(data.begin(), data.end(), [](int x) { return x < 0; });
int zero = std::count(data.begin(), data.end(), 0);
std::cout << "=== Statistics ===" << std::endl;
std::cout << "Count: " << count << std::endl;
std::cout << "Sum: " << sum << std::endl;
std::cout << "Mean: " << mean << std::endl;
std::cout << "Min: " << *minIt << std::endl;
std::cout << "Max: " << *maxIt << std::endl;
std::cout << "Even: " << evenCount << ", Odd: " << oddCount << std::endl;
std::cout << "Positive: " << positive << ", Negative: " << negative << ", Zero: " << zero << std::endl;
}
static void validate(const std::vector<int>& data) {
bool allPositive = std::all_of(data.begin(), data.end(), [](int x) {
return x > 0;
});
bool hasNegative = std::any_of(data.begin(), data.end(), [](int x) {
return x < 0;
});
bool noZero = std::none_of(data.begin(), data.end(), [](int x) {
return x == 0;
});
std::cout << "\n=== Validation ===" << std::endl;
std::cout << "All positive: " << std::boolalpha << allPositive << std::endl;
std::cout << "Has negative: " << std::boolalpha << hasNegative << std::endl;
std::cout << "No zero: " << std::boolalpha << noZero << std::endl;
}
static bool inRange(const std::vector<int>& data, int min, int max) {
return std::all_of(data.begin(), data.end(), [min, max](int x) {
return x >= min && x <= max;
});
}
};
int main() {
std::vector<int> data = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
Statistics::analyze(data);
Statistics::validate(data);
std::cout << "\nRange [1, 10]: " << std::boolalpha
<< Statistics::inRange(data, 1, 10) << std::endl;
std::cout << "Range [1, 5]: " << std::boolalpha
<< Statistics::inRange(data, 1, 5) << std::endl;
return 0;
}
Comparison
Signatures
#include <algorithm>
Comparison table
| Algorithm | Return | Time | Short-circuit | Empty range |
|---|---|---|---|---|
| count | difference_type | O(n) | No | 0 |
| count_if | difference_type | O(n) | No | 0 |
| all_of | bool | O(n) | Yes | true |
| any_of | bool | O(n) | Yes | false |
| none_of | bool | O(n) | Yes | true |
count or any_of: pick the one that can stop early
| Goal | Algorithm | Example |
|---|---|---|
| Count value | count | count(v.begin(), v.end(), 5) |
| Count predicate | count_if | count_if(v.begin(), v.end(), isEven) |
| All satisfy | all_of | all_of(v.begin(), v.end(), isPositive) |
| Any satisfies | any_of | any_of(v.begin(), v.end(), isNegative) |
| None satisfy | none_of | none_of(v.begin(), v.end(), isZero) |
The common misuse is count(...) > 0 or count_if(...) != 0 to ask whether something exists. It gives the right answer but always scans the whole range, while any_of (or find) returns at the first match. Use count only when you need the number. For associative containers, prefer the member functions: set.count(x) and, in C++20, set.contains(x) use the tree or hash lookup instead of the linear walk that std::count would do.