¿Por qué no se puede “transformar” (s.begin (), s.end (), s.begin (), tolower) “con éxito?

Dado el código:

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

Me sale el error:

No hay función de coincidencia para llamar a transform(__gnu_cxx::__normal_iterator<char*, std::basic_string<char, std::char_traits, std::allocator > >, __gnu_cxx::__normal_iterator<char*, std::basic_string<char, std::char_traits, std::allocator > >, __gnu_cxx::__normal_iterator<char*, std::basic_string<char, std::char_traits, std::allocator > >, )

¿Qué significa “tipo de función sobrecargada sin resolver” ?

Si reemplazo el tolower con una función que escribí, ya no hay errores.

intenta usar ::tolower . Esto solucionó el problema para mí

El problema probablemente se relaciona con múltiples sobrecargas de tolower y el comstackdor no puede seleccionar uno para usted. Puede intentar calificarlo para seleccionar una versión específica del mismo, o puede necesitar proporcionar un molde de puntero a función para desambiguar. La función tolower puede estar presente (múltiples sobrecargas diferentes) en el encabezado , así como también en .

Tratar:

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

Eso se puede hacer en una sola línea con un molde, pero probablemente sea más difícil de leer:

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

Veamos una lista de opciones que comienza con lo peor y avanza hacia lo mejor. Los enumeraremos aquí y discutiremos a continuación:

  1. transform(cbegin(s), cend(s), begin(s), ::tolower)
  2. transform(cbegin(s), cend(s), begin(s), static_cast(tolower))
  3. transform(cbegin(s), cend(s), begin(s), [](const unsigned char i){ return tolower(i); })

El código en su pregunta, transform(s.begin(), s.end(), s.begin(), tolower) producirá un error como:

No hay función de coincidencia para call to transform(std::basic_string::iterator, std::basic_string::iterator, std::basic_string::iterator, )

La razón por la que obtenía un “tipo de función sobrecargada no resuelta” es que hay dos tolower en el std nombres std :

  1. La biblioteca locale define la template T tolower(T, const locale&)
  2. La biblioteca cctype define int tolower(int)

1 es la solución ofrecida por davka . Soluciona el error aprovechando el hecho de que el tolower locale no está definido en el espacio de nombres global.

Dependiendo de su situación, la tolower puede merecer consideración. Puede encontrar una comparación de los tolower s aquí: ¿Qué tolower en C ++?


Desafortunadamente, 1 depende de que se cctype definido el tolower cctype en el espacio de nombres global. Veamos por qué ese no es el caso:

Está utilizando correctamente #include , ya que hacer #include ha quedado obsoleto en C ++: http://en.cppreference.com/w/cpp/header

Pero el estándar de C ++ establece en D.3 [depr.c.headers] 2 de las declaraciones en los encabezados:

No se especifica si estos nombres se declaran o definen por primera vez dentro del ámbito de espacio de nombres (3.3.6) del espacio de nombres std y luego se inyectan en el ámbito del espacio de nombres global mediante declaraciones de uso explícitas (7.3.3)

Entonces, la única forma en que podemos garantizar que nuestro código sea independiente de la implementación es usar un tolower desde el namespace std de namespace std . 2 es la solución ofrecida por David Rodríguez – dribeas . Aprovecha el hecho de que static_cast puede:

Se usará para eliminar la ambigüedad de las sobrecargas de funciones realizando una conversión de función a puntero a un tipo específico

Antes de continuar, permítame comentar que si encuentra que int (*)(int) es un poco confuso, puede leer más sobre la syntax del puntero a la función aquí .


Tristemente hay otro problema con el argumento de entrada de tolower , si:

No es representable como char sin signo y no es igual a EOF, el comportamiento no está definido

Estás usando una string que usa elementos de tipo: char . Los estados estándar de char específicamente 7.1.6.2 [dcl.type.simple] 3:

Se define en la implementación si los objetos de tipo char se representan como cantidades con signo o sin signo. El especificador signed obliga a los objetos de char a firmar

Por lo tanto, si la implementación define un char como un signed char entonces tanto 1 como 2 daría como resultado un comportamiento indefinido para todos los caracteres correspondientes a números negativos. (Si se utiliza una encoding de caracteres ASCII, los caracteres correspondientes a los números negativos son ASCII extendido .)

El comportamiento indefinido se puede evitar convirtiendo la entrada en un unsigned char antes de pasarlo a tolower . 3 logra que el uso de una lambda que acepta un unsigned char por valor, luego lo pasa a tolower convirtiendo implícitamente a int .

Para garantizar un Comportamiento definido en todas las implementaciones compatibles, independientemente de la encoding de caracteres, deberá usar la transform(cbegin(s), cend(s), begin(s), [](const unsigned char i){ return tolower(i); }) o algo similar.

David ya identificó el problema, a saber, un conflicto entre:

  • ‘s int tolower(int c)
  • template charT tolower(charT c, locale const& loc)

Usar el primero es mucho más fácil, pero es un comportamiento indefinido (desafortunadamente) tan pronto como trabajas con algo más que Ascii inferior (0-127) en los caracteres firmados. Por cierto, recomiendo definir char como unsigned.

La versión de la plantilla sería agradable, pero tendría que usar bind para proporcionar el segundo parámetro, y seguramente será feo …

Entonces, ¿puedo presentar la biblioteca Boost String Algorith m?

Y más importante: boost::to_lower 🙂

 boost::to_lower(s); 

La expresividad es deseable.

Al mi desde gcc 4.2.1, veo esto:

 // -*- 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  #include  // Get rid of those macros defined in  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 

Por lo tanto, parece que tolower existe en los espacios de nombres std (de ) y de raíz (de ). No estoy seguro de lo que hace #pragma .