Почему нельзя "преобразовать (s.begin(), s.end(), s.begin(), tolower)" успешно выполняться?

С учетом кода:

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

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

Я получаю сообщение об ошибке:

Нет соответствующей функции для вызова 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>)

Что означает "неразрешенный перегруженный тип функции?

Если я заменил tolower на функцию, которую я написал, это уже не ошибки.

Ответ 1

попробуйте использовать ::tolower. Это фиксировало проблему для меня

Ответ 2

Проблема, скорее всего, связана с множественными перегрузками tolower, и компилятор не может выбрать один для вас. Вы можете попробовать отдать его, чтобы выбрать конкретную версию, или вам может потребоваться предоставить указатель функции, чтобы устранить неоднозначность. Функция tolower может присутствовать (несколько разных перегрузок) в заголовке <locale>, а также в <cctype>.

Try:

int (*tl)(int) = tolower; // Select that particular overload
transform(s.begin(),s.end(),s.begin(),tl );

Это можно сделать в одной строке с литой, но ее, вероятно, труднее читать:

transform(s.begin(),s.end(),s.begin(),(int (*)(int))tolower );

Ответ 3

Давайте посмотрим на список вариантов, начиная с наихудшего и переходя к лучшему. Мы перечислим их здесь и обсудим их ниже:

  • 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) приведет к ошибке, например:

Нет соответствующей функции для вызова преобразования (std:: basic_string <char>:: iterator, std:: basic_string <char>:: iterator, std:: basic_string <char>:: iterator, < неразрешенные перегруженный тип функции >)

Причина, по которой вы получаете "неразрешенный перегруженный тип функции", есть 2 tolower s в пространстве имен std:

1 - это решение, предлагаемое davka. Он устраняет вашу ошибку, используя тот факт, что locale tolower не определен в глобальном пространстве имен.

В зависимости от вашей ситуации locale tolower может заслуживать рассмотрения. Вы можете найти сравнение tolower здесь: Какой из них в С++?


К сожалению 1 зависит от cctype tolower, который определяется в глобальном пространстве имен. Давайте посмотрим, почему это может быть не так:

Вы справедливо используете #include <cctype>, так как #include < ctype.h > устарел в С++: http://en.cppreference.com/w/cpp/header

Но стандарт С++ указывает в D.3 [des.c.headers] 2 объявления в заголовках:

Неизвестно, объявлены или определены ли эти имена в области пространства имен (3.3.6) пространства имен std и затем они вводятся в область глобального пространства имен с помощью явных использования-деклараций (7.3. 3)

Таким образом, единственный способ гарантировать наш код - независимость от реализации - использовать tolower из namespace std. 2 - это решение, предлагаемое Дэвидом Родригесом - дрибеасом. Он использует тот факт, что static_cast может:

Используется для устранения неоднозначности перегрузок функций путем преобразования функции в указатель для определенного типа

Прежде чем двигаться дальше, позвольте мне прокомментировать, что если вы обнаружите, что int (*) (int) немного запутанно, вы можете больше узнать о синтаксисе указателя функции здесь.


К сожалению, есть еще одна проблема с помощью tolower, если он:

Не представляется без знака char и не равен EOF, поведение не определено

Вы используете строку , которая использует элементы типа: 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 выполняет это, используя lambda, который принимает значение unsigned char по значению, затем передает его в tolower, неявно преобразуясь в int.

Чтобы гарантировать Определенное поведение во всех совместимых реализациях, независимо от кодировки символов, вам нужно будет использовать преобразование (cbegin (s), cend (s), begin (s), [] (const unsigned char i) {return tolower (i);}) или что-то подобное.

Ответ 4

Дэвид уже определил проблему, а именно конфликт между:

  • <cctype> int tolower(int c)
  • <locale> template <typename charT> charT tolower(charT c, locale const& loc)

Использование первого намного проще, но это поведение undefined (к сожалению), как только вы имеете дело с чем-либо еще, чем нижний ascii (0-127) в подписанных символах. Кстати, я рекомендую определять char как unsigned.

Версия шаблона будет приятной, но вам нужно будет использовать bind, чтобы предоставить второй параметр, и он должен быть уродливым...

Итак, могу ли я представить Boost String Algorithm m library?

И что еще более важно: boost::to_lower:)

boost::to_lower(s);

Выразительность желательна.

Ответ 5

Просмотр моего заголовка <ctype> из gcc 4.2.1, я вижу следующее:

// -*- 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

Таким образом, он выглядит как tolower существует как в пространствах имен std (from <cctype>), так и в корне (из <ctype.h>). Я не уверен, что делает #pragma.