Why “transform(s.begin(),s.end(),s.begin(),tolower)” can't be complied successfully?

From: http://stackoverflow.com/questions/5539249/why-transforms-begin-s-end-s-begin-tolower-cant-be-complied-successfu

Why “transform(s.begin(),s.end(),s.begin(),tolower)” can't be complied successfully?

#include<iostream>
#include<cctype>
#include<string>
#include<algorithm>
using namespace std;

main()
{
     string s("ABCDEFGHIJKL");
     transform(s.begin(),s.end(),s.begin(),tolower);
     cout<<s<<endl;
}

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> > >, __gnu_cxx::__normal_iterator<char*, std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, <unresolved overloaded function type>)'`

What the "unresolved overloaded function type" means?

if I replace the tolower of a function written by myself ,it can work.

share | improve this question
 
2 
The return type of main is int, and return types in C++ have to be explicit. Some compilers will allow the code as posted, but it is non-standard, and it might break with the new compiler version or in other compilers. –  David Rodríguez - dribeas Apr 4 '11 at 15:21
up vote 8 down vote accepted

try using ::tolower. This fixed the problem for me

share | improve this answer
 
1 
It's right. What's difference between tolower and ::tolower? –  liu Apr 4 '11 at 14:06
 
@liu: it's as @David wrote - the "::" selects the tolower from the global namespace –  davka Apr 4 '11 at 14:16

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 );
share | improve this answer
 
3 
But don't forget that using this version of tolower on a string like the above is undefined behavior if any of the char values are negative (which they can be on most modern systems, e.g. if any accented characters are present). –  James Kanze Apr 4 '11 at 13:49
1 
@James Kanze: good point, I decided for that overload from reading the original post (where cctype is explicitly included, while locale is not). Also, the functions in locale take more than a single argument, and that means that the code would add unrelated complexity with a bind or bind2nd to provide the default locale... –  David Rodríguez - dribeas Apr 4 '11 at 13:58
 
Thank you.I understand the problem. And using ::tolower can fix the problem –  liu Apr 4 '11 at 14:11
1 
@liu: Note that using ::tolower will work in different compilers, but it is not standard. Basically most compilers, when you include cctype the compiler is required to provide int std::tolower(int), but it is not required to add int ::tolower(int), different compilers will provide both functions with the same implementation (one of them will forward to the other) but that is not required and might change with the next compiler release (or if you change the compiler) –  David Rodríguez - dribeas Apr 4 '11 at 15:20
1 
@liu Using ::tolower doesn't fix the problem. Calling ::tolower with a char argument is undefined behavior. You need to wrap it in a functional object which converts the char to unsigned char. Or buy into all of the complexity that David Rodríguez mentions with regards to the versions in <locale>. –  James Kanze Apr 4 '11 at 16:40

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.

share | improve this answer
 
1 
the pragma signals to gcc that this file is a system header. This should typically affect diagnosis, since it's considered bad style for a compiler to emit warnings for headers that it was bundled with and should not be altered. –  Matthieu M. Apr 4 '11 at 19:12

David already identified the issue, namely a conflict between:

  • <cctype>'s int tolower(int c)
  • <locale>'s template <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.

share | improve this answer
 
 
1) can you please explain what do you mean by I do recommend defining char as unsigned? 2) does boost::to_lower assumes some character set, e.g. latin-1? –  davka Apr 5 '11 at 15:27
 
@davka: 1) the C++ Standard does not precise whether a char is signed or unsigned. You can qualify it if you want to be sure. However a number of functions (like int tolower(int)) have undefined behavior if invoked with a negative char... Look it up on your compiler, there might be a switch or a sane default. 2) boost::to_lower is based on the C++ tolower function, and thus depends on the std::locale and the ctype facet it has been imbued with. Note that these facets cannot handle multi-characters encoding anyway... –  Matthieu M. Apr 5 '11 at 15:37
 
thanks, I still don't get #1. I know about char being implementation-dependent. Are you suggesting typedef unsigned char char? is it legal? –  davka Apr 5 '11 at 15:43
1 
@davka: No, it's not legal. Compilers usually have switches to let you decide, for example gcc has -fsigned-char and -funsigned-char. –  Matthieu M. Apr 5 '11 at 17:00
 
that's interesting, thanks. So, if I use -funsigned-char I should use signed char if I want -128..127, correct? Does it work well with 3rd party libraries that are not compiled with this flag? –  davka Apr 5 '11 at 17:09

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值