Here's the code to find whether a string contains only digits:
#include <iostream>
#include <string>
#include <cctype>
#include <algorithm>
int main() {
std::string s("123");
std::all_of(s.begin(), s.end(), std::isdigit); //error!
}
But the code doesn't compile, with the following errors (produced using clang++
):
error: no matching function for call to 'all_of'
note: candidate template ignored: couldn't infer template argument '_Predicate'
all_of(_InputIterator __first, _InputIterator __last, _Predicate __pred)
When I replace std::isdigit
with isdigit
, the code compiles without errors.
#include <iostream>
#include <string>
#include <cctype>
#include <algorithm>
int main() {
std::string s("123");
std::all_of(s.begin(), s.end(), isdigit); //OK
}
I tried the above on both Apple clang
and linux g++
, and got similar errors.
I know that isdigit
is from the C header ctype.h
, and that std::isdigit
is a wrapper defined in C++ header cctype
, so essentially they should be the same. So what are the differences between isdigit
and std::isdigit
?
In C++, you are not allowed to take the address of most standard library functions:
https://en.cppreference.com/w/cpp/language/extending_std#Addressing_restriction
The behavior of a C++ program is unspecified (possibly ill-formed) if it explicitly or implicitly attempts to form a pointer, reference (for free functions and static member functions) or pointer-to-member (for non-static member functions) to a standard library function or an instantiation of a standard library function template, unless it is designated an addressable function
std::isdigit()
is not an addressable standard library function, so you can't use it directly as an algorithm predicate.
That begin said, even if you could pass std::isdigit()
directly as a predicate, doing so would risk undefined behavior:
https://en.cppreference.com/w/cpp/string/byte/isdigit
Like all other functions from
<cctype>
, the behavior ofstd::isdigit
is undefined if the argument's value is neither representable asunsigned char
nor equal toEOF
. To use these functions safely with plainchar
s (orsigned char
s), the argument should first be converted tounsigned char
:bool my_isdigit(char ch) { return std::isdigit(static_cast<unsigned char>(ch)); }
Similarly, they should not be directly used with standard algorithms when the iterator's value type is
char
orsigned char
. Instead, convert the value tounsigned char
first:int count_digits(const std::string& s) { return std::count_if(s.begin(), s.end(), // static_cast<int(*)(int)>(std::isdigit) // wrong // [](int c){ return std::isdigit(c); } // wrong // [](char c){ return std::isdigit(c); } // wrong [](unsigned char c){ return std::isdigit(c); } // correct ); }
As such, since you need to convert each char
to unsigned char
and back, you should call std::isdigit()
inside of a lambda that is used as the predicate for std::all_of()
, eg:
std::all_of(s.begin(), s.end(),
[](unsigned char ch){ return std::isdigit(ch); }
);