Compiles with tolower from global namespace but not with std::tolower

E

Eric Lilja

Hello, consider this program:
#include <iostream>
#include <algorithm>
#include <string>
#include <cctype>

int
main()
{
std::string s = "HEJ";
std::transform (s.begin(), s.end(), s.begin(), std::tolower);
std::cout << "s: " << s << std::endl;
}

It doesn't compile, I get:
$ make
g++ -Wall -W -ansi -pedantic -g -c -o issue.o issue.cpp
issue.cpp: In function `int main()':
issue.cpp:10: 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> > >, <unknown type>)'
make: *** [issue.o] Error 1
¨
*but*, if I change the call to std::transform to
std::transform (s.begin(), s.end(), s.begin(), ::tolower);
(i.e., I pass ::tolower instead of std::tolower)

it compiles cleanly and produces the expected output when run. So it
works with the tolower that lives in the global namespace, why?

Is this the correct behaviour? Someone said that the first version
compiles cleanly on visual studio (unknown version) but I haven't had
the chance to try myself.

/ Eric
 
M

Marcin Kalicinski

int
main()
{
std::string s = "HEJ";
std::transform (s.begin(), s.end(), s.begin(), std::tolower);
std::cout << "s: " << s << std::endl;
}

std::tolower takes 2 arguments, not one. The second is locale. You can use
bind2nd to adapt it.

cheers,
Marcin
 
T

Thomas Tutone

Eric said:
Hello, consider this program:
#include <iostream>
#include <algorithm>
#include <string>
#include <cctype>

int
main()
{
std::string s = "HEJ";
std::transform (s.begin(), s.end(), s.begin(), std::tolower);
std::cout << "s: " << s << std::endl;
}

It doesn't compile, I get:

[error message snipped]
$ make
*but*, if I change the call to std::transform to
std::transform (s.begin(), s.end(), s.begin(), ::tolower);
(i.e., I pass ::tolower instead of std::tolower)

it compiles cleanly and produces the expected output when run. So it
works with the tolower that lives in the global namespace, why?

Is this the correct behaviour?

This comes up so often it really should be in the FAQ. Search for
transform and tolower in google groups and you'll see the answer (many,
many times):

http://groups.google.com/groups?q=transform+tolower

Best regards,

Tom
 
J

John Harrison

Thomas said:
Eric Lilja wrote:

Hello, consider this program:
#include <iostream>
#include <algorithm>
#include <string>
#include <cctype>

int
main()
{
std::string s = "HEJ";
std::transform (s.begin(), s.end(), s.begin(), std::tolower);
std::cout << "s: " << s << std::endl;
}

It doesn't compile, I get:


[error message snipped]

$ make
*but*, if I change the call to std::transform to
std::transform (s.begin(), s.end(), s.begin(), ::tolower);
(i.e., I pass ::tolower instead of std::tolower)

it compiles cleanly and produces the expected output when run. So it
works with the tolower that lives in the global namespace, why?

Is this the correct behaviour?


This comes up so often it really should be in the FAQ. Search for
transform and tolower in google groups and you'll see the answer (many,
many times):

http://groups.google.com/groups?q=transform+tolower

Best regards,

Tom

It should also be in the FAQ that to use ::tolower in this form is to
risk undefined behaviour. It is undefined behaviour if ::tolower is
passed a negative value (excepting the value of EOF). But since char
*may* be signed you are rsking that undefined behaviour. The correct
form is something like this

inline int safe_tolower(char ch)
{
return ::tolower((unsigned char)ch);
}

std::transform (s.begin(), s.end(), s.begin(), safe_tolower);

John
 
T

Thierry Miceli

std::tolower takes 2 arguments, not one. The second is locale. You can
use bind2nd to adapt it.
Correct me if I'm wrong but I don't think that you can use bind2nd whith
std::tolower since the locale is passed by reference and not by value.

You may want to define your own functor:

struct ToLower
{
char operator()(char c)
{
return std::tolower(c, loc_);
}
private:
std::locale loc_; // default locale of the user environment
};

and use it like that:

std::transform(s.begin(), s.end(), s.begin(), ToLower());
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,056
Latest member
GlycogenSupporthealth

Latest Threads

Top