Why can't "transform(s.begin(),s.end(),s.begin(),tolower)" be complied successfully?
Given the code:
#include <iostream>
#include <cctype>
#include <string>
#include <algorithm>
using namespace std;
int main()
{
string s("ABCDEFGHIJKL");
transform(s.begin(),s.end(),s.begin(),tolower);
cout<<s<<endl;
}
I get the error:
No matching function for call to
transform(__gnu_cxx::__normal_iterator<char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, __gnu_cxx::__normal_iterator<char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > &开发者_JAVA技巧gt;, __gnu_cxx::__normal_iterator<char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, <unresolved overloaded function type>)
What does "unresolved overloaded function type" mean?
If I replace the tolower
with a function I wrote, it no longer errors.
Let's look at a list of options starting with the worst and moving to the best. We'll list them here and discuss them below:
transform(cbegin(s), cend(s), begin(s), ::tolower)
transform(cbegin(s), cend(s), begin(s), static_cast<int(*)(int)>(tolower))
transform(cbegin(s), cend(s), begin(s), [](const unsigned char i){ return tolower(i); })
The code in your question, transform(s.begin(), s.end(), s.begin(), tolower)
will produce an error like:
No matching function for call to
transform(std::basic_string<char>::iterator, std::basic_string<char>::iterator, std::basic_string<char>::iterator, <unresolved overloaded function type>)
The reason that you were getting an "unresolved overloaded function type" is there are 2 tolower
s in the std
namespace:
- The
locale
library definestemplate <typename T> T tolower(T, const locale&)
- The
cctype
library definesint tolower(int)
1 is the solution offered by davka. It addresses your error by leveraging the fact that locale
's tolower
is not defined in the global namespace.
Depending upon your situation locale
's tolower
may merit consideration. You can find a comparison of the tolower
s here: Which tolower in C++?
Unfortunately 1 depends upon cctype
's tolower
being defined in the global namespace. Let's look at why that may not be the case:
You are rightly using #include <cctype>
, as doing #include <ctype.h>
has been deprecated in C++: http://en.cppreference.com/w/cpp/header
But the C++ standard states in D.3[depr.c.headers]2 of the declarations in the headers:
It is unspecified whether these names are first declared or defined within namespace scope (3.3.6) of the namespace
std
and are then injected into the global namespace scope by explicit using-declarations (7.3.3)
So the only way that we can guarantee our code is implementation independent is to use a tolower
from namespace std
. 2 is the solution offered by David Rodríguez - dribeas. It leverages the fact that static_cast
can:
Be used to disambiguate function overloads by performing a function-to-pointer conversion to specific type
Before we move on, let me comment that if you find int (*)(int)
to be a bit confusing you can read more on function pointer syntax here.
Sadly there is one other issue with tolower
's input argument, if it:
Is not representable as unsigned char and does not equal EOF, the behavior is undefined
You are using a string
which uses elements of type: char
. The standard states of char
specifically 7.1.6.2[dcl.type.simple]3:
It is implementation-defined whether objects of
char
type are represented as signed or unsigned quantities. Thesigned
specifier forceschar
objects to be signed
So if the implementation defined a char
to mean a signed char
then both 1 and 2 would result in Undefined Behavior for all characters corresponding to negative numbers. (If an ASCII character encoding is being used the characters corresponding to negative numbers are Extended ASCII.)
The Undefined Behavior can be avoided by converting the input to an unsigned char
before passing it to tolower
. 3 accomplishes that using a lambda that accepts an unsigned char
by value, then passes it to tolower
implicitly converting to int
.
To guarantee Defined Behavior on all compliant implementations, independent of character encoding, you'll need to use transform(cbegin(s), cend(s), begin(s), [](const unsigned char i){ return tolower(i); })
or something similar.
Try using ::tolower
. This fixed the problem for me.
The problem most probably relates with multiple overloads of tolower
and the compiler is unable to select one for you. You can try qualifying it to select an specific version of it, or you might need to provide a function pointer cast to disambiguate. The tolower
function can be present (multiple different overloads) in the <locale>
header, as well as in <cctype>
.
Try:
int (*tl)(int) = tolower; // Select that particular overload
transform(s.begin(),s.end(),s.begin(),tl );
That can be done in a single line with a cast, but it is probably harder to read:
transform(s.begin(),s.end(),s.begin(),(int (*)(int))tolower );
David already identified the issue, namely a conflict between:
<cctype>
'sint tolower(int c)
<locale>
'stemplate <typename charT> charT tolower(charT c, locale const& loc)
Using the first is much easier, but is undefined behavior (unfortunately) as soon as you deal with anything else than lower-ascii (0-127) in signed chars. By the way, I do recommend defining char
as unsigned.
The template version would be nice, but you would have to use bind
to provide the second parameter, and it's bound to be ugly...
So, may I introduce the Boost String Algorithm library ?
And more importantly: boost::to_lower
:)
boost::to_lower(s);
Expressiveness is desirable.
Browsing my <ctype>
header from gcc 4.2.1, I see this:
// -*- C++ -*- forwarding header.
// Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
// Free Software Foundation, Inc.
...
#ifndef _GLIBCXX_CCTYPE
#define _GLIBCXX_CCTYPE 1
#pragma GCC system_header
#include <bits/c++config.h>
#include <ctype.h>
// Get rid of those macros defined in <ctype.h> in lieu of real functions.
#undef isalnum
#undef isalpha
...
#undef tolower
#undef toupper
_GLIBCXX_BEGIN_NAMESPACE(std)
using ::isalnum;
using ::isalpha;
...
using ::tolower;
using ::toupper;
_GLIBCXX_END_NAMESPACE
#endif
So it looks like tolower
exists in both the std
(from <cctype>
) and root (from <ctype.h>
) namespaces. I'm not sure what the #pragma
does.
精彩评论