Давайте посмотрим на список вариантов, начиная с худшего и переходя к лучшему. Мы перечислим их здесь и обсудим ниже:
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); })
Код в вашем вопросе transform(s.begin(), s.end(), s.begin(), tolower)
выдаст ошибку вроде:
Нет соответствующей функции для вызова transform(std::basic_string<char>::iterator, std::basic_string<char>::iterator, std::basic_string<char>::iterator, <unresolved overloaded function type>)
Причина, по которой вы получили «неразрешенный тип перегруженной функции», состоит в том, что в пространстве имен std
есть 2 tolower
:
- Библиотека
locale
определяет template <typename T> T tolower(T, const locale&)
- Библиотека
cctype
определяет int tolower(int)
1 - это решение , предлагаемое davka . Она устраняет вашу ошибку, используя тот факт, что locale
tolower
не определено в глобальном пространстве имен.
В зависимости от вашей ситуации locale
tolower
может заслуживать рассмотрения. Вы можете найти сравнение tolower
здесь: Какой цвет в C ++?
К сожалению, 1 зависит от cctype
tolower
, определяемого в глобальном пространстве имен. Давайте посмотрим, почему это не так:
Вы правильно используете #include <cctype>
, поскольку #include <ctype.h>
устарел в C ++: http://en.cppreference.com/w/cpp/header
Но стандарт C ++ в D.3 [depr.c.headers] 2 гласит объявления в заголовках:
Не определено, были ли эти имена впервые объявлены или определены в пределах области имен (3.3.6) пространства имен std
и затем введены в глобальную область имен с помощью явных объявлений using (7.3.3)
Таким образом, единственный способ гарантировать, что наш код независим от реализации, - это использовать tolower
из namespace std
. 2 - это решение , предложенное Дэвидом Родригесом - dribeas . Он использует тот факт, что static_cast
может:
Используется для устранения неоднозначности перегрузок функций путем выполнения преобразования функции в указатель в конкретный тип
Прежде чем мы продолжим, позвольте мне прокомментировать, что если вы найдете int (*)(int)
немного запутанным, вы можете прочитать больше о синтаксисе указателя на функцию здесь .
К сожалению, есть еще одна проблема с входным аргументом tolower
, если она:
не может быть представлен как unsigned char и не равен EOF, поведение не определено
Вы используете string
, который использует элементы типа: char
. Стандартные состояния char
, в частности 7.1.6.2 [dcl.type.simple] 3:
Определяется реализацией, представляются ли объекты типа char
в виде количеств со знаком или без знака. Спецификатор signed
заставляет char
объекты быть подписанными
Таким образом, если реализация определила char
как signed char
, то и 1 и 2 приведут к неопределенному поведению для всех символов, соответствующих отрицательным числам. (Если используется кодировка символов ASCII, символы, соответствующие отрицательным числам, равны Extended ASCII .)
Неопределенного поведения можно избежать путем преобразования ввода в unsigned char
перед передачей его в tolower
. 3 выполняет, используя лямбду, которая принимает значение unsigned char
по значению, а затем передает его tolower
, неявно преобразуя в int
.
Чтобы гарантировать определенное поведение во всех совместимых реализациях, независимо от кодировки символов, вам необходимо использовать transform(cbegin(s), cend(s), begin(s), [](const unsigned char i){ return tolower(i); })
или что-то подобное.