c++ - ¿Por qué no se puede cumplir con éxito "transform(s.begin(), s.end(), s.begin(), tolower)"?
compiler-errors lowercase (5)
David ya identificó el problema, es decir, un conflicto entre:
-
<cctype>
deint tolower(int c)
-
<locale>
template <typename charT> 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 se trata de algo más que ascii inferior (0-127) en caracteres firmados. Por cierto, recomiendo definir char
como unsigned.
La versión de la plantilla estaría bien, pero tendría que usar el bind
para proporcionar el segundo parámetro, y está destinado a ser feo ...
Entonces, ¿puedo presentar la biblioteca Boost String Algorith m?
Y lo que es más importante: boost::to_lower
:)
boost::to_lower(s);
La expresividad es deseable.
Dado el código:
#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 hay función coincidente para la llamada a
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é significa "tipo de función sobrecargada sin resolver" ?
Si sustituyo el tolower
con una función que escribí, ya no se producen errores.
El problema probablemente se relaciona con múltiples sobrecargas de tolower
y el compilador no puede seleccionar uno por ti. Puede intentar calificarlo para seleccionar una versión específica del mismo, o tal vez deba proporcionar un puntero de función para desambiguar. La función tolower
puede estar presente (múltiples sobrecargas diferentes) en el encabezado <locale>
, así como en <cctype>
.
Tratar:
int (*tl)(int) = tolower; // Select that particular overload
transform(s.begin(),s.end(),s.begin(),tl );
Esto se puede hacer en una sola línea con un reparto, pero probablemente sea más difícil de leer:
transform(s.begin(),s.end(),s.begin(),(int (*)(int))tolower );
Navegando por mi <ctype>
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 <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
Así que parece que tolower
existe tanto en los espacios de nombres std
(de <cctype>
) como de root (de <ctype.h>
). No estoy seguro de lo que hace la #pragma
.
Trate de usar ::tolower
. Esto solucionó el problema para mí.
Veamos una lista de opciones que comienzan con lo peor y avanzan hacia lo mejor. Los listaremos aquí y los discutiremos a continuación:
-
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); })
El código en su pregunta, transform(s.begin(), s.end(), s.begin(), tolower)
producirá un error como:
No hay función coincidente para la llamada a la
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 estaba obteniendo un "tipo de función sobrecargada sin resolver" es que hay 2 tolower
de tolower
en el std
nombres std
:
- La biblioteca de
locale
define latemplate <typename T> T tolower(T, const locale&)
- La biblioteca
cctype
defineint tolower(int)
1 es la solución ofrecida por davka . Aborda su error aprovechando el hecho de que la tolower
locale
no está definida en el espacio de nombres global.
Dependiendo de la situación de su locale
, puede ser tolower
consideración. Puede encontrar una comparación de los tolower
s aquí: ¿Qué tolower en C ++?
Desafortunadamente, 1 depende de la cctype
de tolower
cctype
en el espacio de nombres global. Veamos por qué ese puede no ser el caso:
Está utilizando correctamente #include <cctype>
, ya que hacer #include <ctype.h>
ha quedado en desuso 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 se definen primero dentro del alcance del espacio de nombres (3.3.6) del espacio de nombres
std
y luego se inyectan en el alcance del espacio de nombres global mediante declaraciones de uso explícitas (7.3.3)
Por lo tanto, la única forma en que podemos garantizar que nuestro código es independiente de la implementación es usar un tolower
de namespace std
. 2 es la solución ofrecida por David Rodríguez - dribeas . Aprovecha el hecho de que static_cast
puede:
Utilícese para desambiguar 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 sintaxis del puntero de función here .
Lamentablemente, hay otro problema con el argumento de entrada de tolower
, si:
No se puede representar como char sin signo y no es igual a EOF, el comportamiento no está definido
Está utilizando una string
que utiliza elementos de tipo: char
. Los estados estándar de char
específicamente 7.1.6.2 [dcl.type.simple] 3:
Se define por la implementación si los objetos de tipo
char
se representan como cantidades firmadas o no firmadas. El especificadorsigned
obliga a los objetoschar
a ser firmados
Entonces, si la implementación define un char
para que signifique un signed char
entonces 1 y 2 darán como resultado un comportamiento indefinido para todos los caracteres correspondientes a números negativos. (Si se utiliza una codificación 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 un lambda que acepta un unsigned char
por valor, luego lo pasa a una conversión implícita a int
.
Para garantizar el comportamiento definido en todas las implementaciones compatibles, independientemente de la codificación de caracteres, deberá usar la transform(cbegin(s), cend(s), begin(s), [](const unsigned char i){ return tolower(i); })
o algo similar.