8

I want to count all numbers in string in shortest code way. I tried that way:

#include <string>
#include <algorithm>

unsigned countNumbers(const std::string s) {
    return count_if(s.begin(), s.end(), isdigit);
}

Error message is:

a.cc: In function ‘unsigned int countNumbers(std::string)’:
a.cc:5:45: error: no matching function for call to ‘count_if(std::basic_string<char>::const_iterator, std::basic_string<char>::const_iterator, <unresolved overloaded function type>)’
a.cc:5:45: note: candidate is:
/usr/include/c++/4.6/bits/stl_algo.h:4607:5: note: template<class _IIter, class _Predicate> typename std::iterator_traits<_InputIterator>::difference_type std::count_if(_IIter, _IIter, _Predicate)

I know that count_if() wants function like: bool (*f)(char); as a third argument, so I tried to cast the function:

unsigned countNumbers(const std::string s) {
    return count_if(s.begin(), s.end(), reinterpret_cast<bool (*)( char )>(isdigit));
}

Error message is:

a.cc: In function ‘unsigned int countNumbers(std::string)’:
a.cc:5:80: error: overloaded function with no contextual type information

I tried also a bit longer version, which gives the same compilation error:

unsigned countNumbers(const std::string s) {
    typedef bool ( * f_ptr )( char );
    f_ptr ptr = reinterpret_cast<f_ptr>(isdigit);
    return count_if(s.begin(), s.end(), ptr);
}

The solution that I want to avoid is to create a function which would be an adapter:

#include <string>
#include <algorithm>

bool is_digit(char c) {
    return isdigit(c);
}

unsigned countNumbers(const std::string s) {
    return count_if(s.begin(), s.end(), is_digit);
}

My question is how can I use functions int(*f)(int) in std::algorithm's functions which want bool(*f)(int) without creating adapter-functions and without using lambda expressions?

I have more issues which would be solved when I get know how to solve the problem, e.g.:

  • Check if string is printable: find_if_not(s.begin(), s.end(), isprint)
  • Check if string contains ",.!?...": find_if (s.begin(), s.end(), ispunct) and more...

I just want to know how to have much more string possibilities in standard C++ thanks to std::algorithms I was searching at the Internet long time, I found similar problem, but I found no solution

Community
  • 1
  • 1
baziorek
  • 2,502
  • 2
  • 29
  • 43
  • Can't reproduce the error, works fine with g++ 4.7.2 (Win7x64, MinGW). **edit**: fails if I use `std::isdigit` instead of `isdigit`. – Zeta Feb 23 '13 at 11:16
  • If you're talking about functions from `` header, they all [take an `int` and return an `int`](http://en.cppreference.com/w/cpp/string/byte/isdigit). – jrok Feb 23 '13 at 11:19
  • Passing a function that returns `int` as the predicate to an algorithm is not an issue. Algorithms are specified in terms of the operations that they apply, not the signatures of their predicates. That is, `if (f(x)) ...` does **not** require that `f` return `bool`, only that its return type be contextually convertible to `bool`. A return type of `int` works fine here. The error message is saying that it can't choose between the overloaded versions of `isdigit`, not that `isdigit` has the wrong return type. – Pete Becker Feb 23 '13 at 12:56
  • The answer gave the solution but not the cause: there is some `std::isdigit` declared in `` (`template bool isdigit(_CharT, const locale&);`) You can still use `isdigit` from `` which is not in `std` (as @Zeta) to avoid the ambiguity. As long as there is one overload of the function, it looks like the compiler cannot infer the possible function's signatures from its usage inside the algorithm functions. I've read http://en.cppreference.com/w/cpp/language/template_argument_deduction (check overload set) but it doesn't state it explicitly. – hsandt Feb 22 '18 at 18:26

4 Answers4

6

You can resolve the function by using a static cast. Alternatively if this is something you want to do a lot you can use a template to resolve it:

#include <string>
#include <cctype>
#include <algorithm>

unsigned count(const std::string& s) {
  return std::count_if(s.begin(), s.end(), static_cast<int(*)(int)>(std::isdigit));
}

template <int(*Pred)(int)> 
unsigned foo(const std::string& s) {
  return std::count_if(s.begin(), s.end(), Pred);
}

int main() {
  count("");
  foo<std::isdigit>("");
  foo<std::isprint>("");
}

static_cast is the "usual" way of resolving ambiguous - it always does what you expect and can be part of a larger expression.

Flexo
  • 87,323
  • 22
  • 191
  • 272
3

Resolve the type by using a function pointer:

unsigned countNumbers(const std::string s) {
    int (*isdigit)(int) = std::isdigit;
    return count_if(s.begin(), s.end(), isdigit);
}

Don't forget to include <cctype>. (demo)

Zeta
  • 103,620
  • 13
  • 194
  • 236
2

I found following also works:

    #include <ctype.h>
    count_if(s.begin(), s.end(), ::isdigit); //explicitly select the C version of isdigit 

but i have to figure out that it works only if the C version is defined as a function rather than a macro

thus, well, the static_cast of std::isdigit could be the best portable solution among platforms.

Moduki
  • 21
  • 2
2

I'll give 2 other solutions to fix the ambiguity with <locale> std::isdigit

  1. Use a lambda expression:

    std::count_if(s.begin(), s.end(), [](char c){ return std::isdigit(c); })

or with explicit casting:

std::count_if(s.begin(), s.end(), [](char c){ return std::isdigit(static_cast<int>(c)) != 0; })
  1. Use explicit template types (you also need to write the iterator type):

std::count_if<std::string::const_iterator, int(*)(int)>(s.begin(), s.end(), std::isdigit)

hsandt
  • 709
  • 7
  • 12