Whats going on here with cctype?
Asked Answered
D

3

15

To my surprise the following code compiles:

#include <iostream>
#include <string>
#include <algorithm>
#include <iterator>
#include <cctype>

int main() {
   std::string s="nawaz";
   std::string S;
   std::transform(s.begin(),s.end(), std::back_inserter(S), ::toupper);
   std::cout << S ;
}

I had expected it to fail because of the ::toupper which I believed should be in the std namespace. A quick check of cctype shows that it is but it is imported from the root namesapce (Mystery solved there).

namespace std
{
  // Other similar `using` deleted for brevity.
  using ::toupper;
}

So first problem solved but if I change the transform() line above too:

std::transform(s.begin(),s.end(), std::back_inserter(S), std::toupper);

I would now expect this to now also compile. But I get a compiler error:

kk.cpp:12: 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::cha r_traits<char>, std::allocator<char> > >, std::back_insert_iterator<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, <unresolved overloaded function type>)'

Which with manual editing resolved too:

kk.cpp:12: error: no matching function for call to
         `transform(iterator<std::string>,
                    iterator<std::string>,
                    std::back_insert_iterator<std::string>,
                    <unresolved overloaded function type>)'

What am I missing?

Devisable answered 23/3, 2011 at 18:18 Comment(6)
+1 for asking this question. Even I want to know!Beutner
Looks like a GCC bug to me. I have no problem compiling it in VC.Brainchild
Ah, you just can't help but smile at those g++ outputs. So full of love, they are.Baby
I did some research thinking it was just going to be like std::toupper and ::toupper are different in some suttle way which transform doesn't handle but I couldn't find anything.Gemsbok
Comeau Online compiles the code as well and so does Intel C++Kalikow
@Santiago Lezica: Not that hard to read (Add a bit of formatting).Devisable
P
11

It doesn't work because there are overloads of std::toupper. You can fix it by casting to your desired function overload:

std::transform(s.begin(),s.end(), std::back_inserter(S),
                (int(&)(int))std::toupper);
Pentheam answered 23/3, 2011 at 19:0 Comment(7)
This is a better answer. Found a little similar discussion at usenet too.Kalikow
Well there is an argument for introducing a new version of all the cctype function for C++ (that take and return char) if ever I saw one. These function accept and return int so that they can deal with EOF character.Devisable
Also shows I should learn to read the error message. It explains the exact problem.Devisable
@GMan: I think the question is not what should be done to make it work; after all ::toupper already works. So the question is, why does std::toupper not work? Should it not work as we clearly mentioned the namespace std?Beutner
@Nawaz: Like I said, because std::toupper has overloads.Pentheam
@GMan: Within std namespace?Beutner
@Nawaz: Yes, from importing ::toupper and from <locale>: template <typename C> C toupper(C c, const locale& l);. Just specifying std::toupper isn't valid for an argument, because it could be either. By casting, it eliminates one overload.Pentheam
E
4

You're missing that C++ also adds new toupper functions in <locale> which is probably included implicitly by one of your other headers. Thus in the std:: namespace there are multiple overloads while in the global namespace there is only the old C version of the function.

That said it does still seem like g++ should be able to deduce the correct overload.

Epirogeny answered 23/3, 2011 at 18:52 Comment(0)
N
2

Like others said the problem is that std::toupper and friends are overloaded. One way to fix this is to use a lambda.

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

int main()
{
   std::string s{ "This is a test" };
   std::string S{};
   std::transform(s.begin(), s.end(), std::back_inserter(S), [] (char ch){ return std::toupper(ch); });
   std::cout << S;
   return 0;
}

I realize this is an old post but as many, including myself, still encounter this issue, I hope my post will be helpful.

Nisus answered 16/7, 2022 at 15:26 Comment(1)
Nice. This would also resolve the issue.Devisable

© 2022 - 2024 — McMap. All rights reserved.