2011-04-04 10 views
23

Teniendo en cuenta el código:¿Por qué no se puede "transformar" (s.begin(), s.end(), s.begin(), tolower) "con éxito?

#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; 
} 

me sale el 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::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>)

¿Qué "sin resolver sobrecargado tipo de función" quiere decir?

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

+3

El tipo de retorno de 'main' es' int', y los tipos de retorno en C++ tienen que ser explícitos. Algunos compiladores permitirán que el código se publique, pero no es estándar, y podría romperse con la nueva versión del compilador o en otros compiladores. –

+1

@ DavidRodríguez-dribeas C o C++ no requieren un retorno de 'main', devuelve implícitamente 0. Ver los comentarios de esta respuesta: http://stackoverflow.com/a/33442842/2642059 –

Respuesta

27

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

+3

está justo. ¿Cuál es la diferencia entre tolower y :: tolower? – liu

+2

@liu: es como escribió @ David - el "::" selecciona el 'tolower' desde el espacio de nombres global – davka

22

El problema probablemente se relaciona con múltiples sobrecargas de tolower y el compilador 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 <locale>, así como en <cctype>.

Probar:

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

que se pueden hacer en una sola línea con un yeso, pero es probable que sea más difícil de leer:

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

Pero no lo olvides que usar esta versión de tolower en una cadena como la anterior es un comportamiento indefinido si alguno de los valores de char son negativos (que pueden estar en la mayoría de los sistemas modernos, por ejemplo, si hay algunos caracteres acentuados). –

+1

@James Kanze: buen punto, decidí por esa sobrecarga al leer la publicación original (donde cctype está explícitamente incluido, mientras que la configuración regional no). Además, las funciones en la configuración regional toman más de un argumento, y eso significa que el código agregaría una complejidad no relacionada con un 'bind' o' bind2nd' para proporcionar el 'locale' predeterminado ... ... –

+0

Gracias. Comprendo el problema . Y usando :: tolower puede solucionar el problema – liu

4

Navegando mi cabecera <ctype> de 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 <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 

Parece que tolower existe en los espacios de nombres std (de <cctype>) y raíz (de <ctype.h>). No estoy seguro de qué hace el #pragma.

+1

las señales pragma a gcc que este fichero es una cabecera de sistema. Esto normalmente debería afectar el diagnóstico, ya que se considera un mal estilo para un compilador emitir advertencias para los encabezados con los que se incluyó y no se debe modificar. –

6

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

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

usando la primera es mucho más fácil, pero es un comportamiento indefinido (desafortunadamente) tan pronto como trabajes con algo más que Ascii inferior (0-127) en caracteres firmados. Por cierto, recomiendo definir char como sin firmar.

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

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

Y lo más importante: boost::to_lower :)

boost::to_lower(s); 

La expresividad es deseable.

+0

1) ¿Puedes explicar a qué te refieres con 'Yo recomiendo definir char como unsigned'? 2) does 'boost :: to_lower' asume algún conjunto de caracteres, p. latin-1? – davka

+0

@davka: 1) el estándar C++ no precisa si un 'char' está firmado o no. Puedes calificar si quieres estar seguro. Sin embargo, varias funciones (como 'int tolower (int)') tienen un comportamiento indefinido si se invoca con un 'char' negativo ... Busque en su compilador, puede haber un cambio o un valor por defecto. 2) 'boost :: to_lower' se basa en la función' tolower' de C++, y por lo tanto depende de 'std :: locale' y de la faceta' ctype' con la que ha sido imbuido. Tenga en cuenta que estas facetas no pueden manejar la codificación de varios caracteres de todos modos ... –

+0

gracias, todavía no obtengo el n. ° 1. Sé que 'char' depende de la implementación. ¿Estás sugiriendo 'typedef unsigned char char'? es legal? – davka

7

Veamos una lista de opciones que comienzan con las peores y avanzan hacia las mejores. Tendremos una lista aquí y se discuten a continuación:

  1. transform(cbegin(s), cend(s), begin(s), ::tolower)
  2. transform(cbegin(s), cend(s), begin(s), static_cast<int(*)(int)>(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 matching function for call to transform(std::basic_string<char>::iterator, std::basic_string<char>::iterator, std::basic_string<char>::iterator, <unresolved overloaded function type>)

La razón por la que usted estaba recibiendo un "sin resolver sobrecargado tipo de función" es que hay 2 tolower s en el std espacio de nombres:

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

es el solution offered by davka. Soluciona el error aprovechando el hecho de que locale 's tolower no está definido en el espacio de nombres global.

Dependiendo de su situación locale 's tolower puede ameritar consideración. Se puede encontrar una comparación de los tolower s aquí: Which tolower in C++?


Desafortunadamente depende de cctype 's tolower siendo definida en el espacio de nombres global. Veamos por qué esto puede no ser el caso:

Usted está utilizando con razón #include <cctype>, como hacer #include <ctype.h> ya no se utiliza en C++: http://en.cppreference.com/w/cpp/header

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

It is unspecified whether these names are first declared or defined within namespace scope (3.3.6) of the namespace std and are then injected into the global namespace scope by explicit using-declarations (7.3.3)

Así que la única manera que podemos garantizar nuestro código es independiente de la implementación es utilizar un tolower de namespace std. es el solution offered by David Rodríguez - dribeas.Se aprovecha el hecho de que puede static_cast:

Be used to disambiguate function overloads by performing a function-to-pointer conversion to specific type

Antes de continuar, permítanme comentar que si encuentra int (*)(int) a ser un poco confuso se puede leer más en función de la sintaxis de puntero here.


Lamentablemente no es one other issue con el argumento de entrada tolower 's, si:

Is not representable as unsigned char and does not equal EOF, the behavior is undefined

está utilizando un string que utiliza elementos de tipo: char. Los estados estándar de char específicamente 7.1.6.2 [dcl.type.simple] 3:

It is implementation-defined whether objects of char type are represented as signed or unsigned quantities. The signed specifier forces char objects to be signed

lo tanto, si la aplicación define una char para significar un signed char entonces tanto y daría lugar a un comportamiento indefinido para todos los caracteres que corresponden a números negativos. (Si una codificación de caracteres ASCII se utiliza los caracteres correspondientes a números negativos se Extended ASCII.)

El comportamiento indefinido se puede evitar mediante la conversión de la entrada a un unsigned char antes de pasarla a tolower. logra que utilizando un lambda que acepta un unsigned char por valor, luego lo pasa a tolower convirtiendo implícitamente a int.

Para garantizar comportamiento definido en todas las implementaciones compatibles, independiente de la codificación de caracteres, tendrá que utilizar o algo similar.

+0

@Downvoter ¿Algún fundamento detrás del voto a favor? –

+2

Me gustaría saber eso también. – exilit

+0

@exilit Creo que esta es la mejor respuesta aquí. Me tomé el tiempo para agregar una respuesta a una pregunta, a pesar de que nunca será aceptada porque sentí que todas las otras respuestas quedaron cortas de una manera u otra. Tener a alguien que simplemente vote "pasar de un automóvil a otro" es ... triste. De todos modos, gracias por la confirmación. Es bueno saber que a alguien le importan mis esfuerzos para mejorar la calidad de las respuestas. –

Cuestiones relacionadas