That's where you should use parametric polymorphism, that's my point.
C++ 23 defines _3_ overloads of string.contains() with parameter defined as variously a char, a char * and a string view, enabling name.contains("Jim") name.contains("Steve"sv) and name.contains('Q'). But if you need a fourth, too bad.
Rust doesn't have overloading, so it defines string.contains() as polymorphic over the parameter type Pattern, which enables name.contains("Jim") name.contains('J') name.contains(char::is_uppercase) name.contains(['B', 'o', 'b']) name.contains(|c| { my_fun(c, 206, local_var) }) and so on and so forth.
One reason C++ doesn't do this is addressed in Circle, by offering "interfaces" which are approximately equivalent to Rust's traits or the C++ 0x Concepts (as Sean firmly points out, these were very different than Stroustrup's C++ 20 Concepts)
The problem is, what does 'Q' have in common with "Jim" ? In conventional C++ polymorphism we want a base class and of course these are basic types, they don't have a base class, much less one which has the necessary commonality.
With interfaces, we can say what we care about isn't some hypothetical "base class" of string_view and char, but instead a common interface they both have dedicated to string matching, and now we're writing polymorphic software again.