Std :: tolower and Visual Studio 2013

I am trying to figure out how to use std::tolower ...

 #include <iostream> #include <string> #include <algorithm> #include <locale> int main() { std::string test = "Hello World"; std::locale loc; for (auto &c : test) { c = std::tolower(c, loc); } std::transform(test.begin(), test.end(), test.begin(), ::tolower); // 1) OK std::transform(test.begin(), test.end(), test.begin(), std::tolower); // 2) Cryptic compile error std::transform(test.begin(), test.end(), test.begin(), static_cast<int(*)(int)>(std::tolower)); // 3) Cryptic compile error. Seems OK with other compilers though return 0; } 

So:

  • Why does ::tolower version work?
  • Why doesn't std::tolower work in std :: transform?
  • What does static_cast<int(*)(int)>(std::tolower)) really try to do? Why does it work with GCC and not with Visual Studio 2013?
  • How could I use std::lower in std :: transform with Visual Studio 2013?
+6
source share
2 answers

First of all, note that none of these approaches does the right thing in a portable way! The problem is that char can be signed (and usually is), but tolower() versions only allow positive values! That is, you really want to use std::tolower() using something like this:

 std::transform(test.begin(), test.end(), test.begin(), [](unsigned char c) { return std::tolower(c); }); 

(or, of course, using the appropriate function object if you are stuck with C ++ 03). Using std::tolower() (or ::tolower() , for that matter) with a negative value results in undefined behavior. Of course, this only matters on the platform where the char signed, which, however, is a typical choice.

To answer your questions:

  • When you turn on <cctype> you usually get various functions and types from the C standard library in both the std namespace and the global namespace. Thus, using ::tolower usually works, but operation is not guaranteed.
  • When you turn on <locale> , two versions of std::tolower , one as int(*)(int) and one as char(*)(char, std::locale const&) . When using only std::tolower , the compiler has no way at all to decide which one to use.
  • Since std::tolower ambiguous, using static_cast<int(*)(int)>(std::tolower) removes the ambiguity of the version used. Why using static_cast<...>() with VC ++ fails, I don't know.
  • You should not use std::tolower() with char sequences, as this will lead to undefined behavior. Use a function object using std::tolower internally on an unsigned char .

It is worth noting that using a functional object, rather than a function pointer, is usually much faster because it is trivial to embed a function object, but not as easy as embedding a function pointer. Compilers are improved by using function pointers, where the function is actually known, but modern compilers, of course, do not always include function calls through function pointers, even if the whole context were there.

+8
source

std::tolower overloaded in C ++, it is declared in <cctype> as

 int tolower(int); 

as well as in <locale> as

 template<CharT> CharT tolower(CharT, const locale&); 

so when you say " std::tolower " you get an ambiguous reference to the overloaded function.

  • Why does ::tolower version work?

When you enable <cctype> , single-port overloads are declared in the std and can also be declared in the global namespace, depending on the compiler. If you include <ctype.h> , then it is guaranteed to be included in the global namespace, and ::tolower will work (although it should be noted that Dietmar indicates when it is unsafe). An overload with two arguments from <locale> never declared in the global namespace, so ::tolower never refers to an overload with two arguments.

2. Why does std::tolower not work in std :: transform?

See above, this is an overloaded name.

3. What does static_cast<int(*)(int)>(std::tolower)) really try to do?

It tells the compiler that you want an int std::tolower(int) overload, not any other std::tolower overload.

Why does it work with GCC and not with Visual Studio 2013?

Probably because you did not include <cctype> or (less likely) this could be a Visual Studio error.

4. How could I use std::lower in std::transform using Visual Studio 2013?

If you know that you only have characters with values ​​from 0 to 127, you can include <ctype.h> and use ::tolower (since the version with two arguments is not declared in the global namespace, only in the std ) or eliminate which overloads you want with a static throw. An alternative to cast is to use a local variable:

 typedef int (*tolower_type)(int); tolower_type tl = &std::tolower; std::transform(b, e, b, tl); 

A safer and portable alternative is to use a custom function object (or lambda expression) to safely invoke the desired overload:

 std::transform(b, e, b, [](unsigned char i) { return std::tolower(i); }); 

In this case, std::tolower with an argument, so the compiler can perform an overload to indicate which overload you want to call. The unsigned char parameter to ensure that we never pass a char with a negative value to tolower(int) , as this has undefined behavior.

See http://gcc.gnu.org/onlinedocs/libstdc++/manual/strings.html#strings.string.simple for more details.

+6
source

Source: https://habr.com/ru/post/957739/


All Articles