lenguaje - librerias estandar de c++
C++, error curioso del compilador al implementar una funciĆ³n `int next(std:: string param)` (4)
(Actualizado según los comentarios de Jonathan) Hay dos vistas aquí, la vista C ++ 11 y la vista C ++ 14. En 2013, std::next
C ++ 11 no estaba correctamente definido. Se supone que se aplica a los iteradores, pero debido a lo que parece ser un descuido, causará fallos graves cuando se pasa un no iterador. Creo que la intención era que SFINAE debería haber prevenido esto; std::iterator_traits<X>
debería causar fallos de sustitución.
En C ++ 14, este problema está resuelto. La definición de std::next
no ha cambiado, pero su segundo argumento ( std::iterator_traits<>
) ahora está correctamente vacío para los no iteradores. Esto elimina std::next
del conjunto de sobrecarga para no iteradores.
La declaración correspondiente (tomada de VS2013) es
template<class _FwdIt> inline
_FwdIt next(_FwdIt _First,
typename iterator_traits<_FwdIt>::difference_type _Off = 1)
Esta función debe agregarse al conjunto de sobrecarga si puede ser instanciada para los argumentos dados.
La función se encuentra a través de la búsqueda dependiente de argumentos y la estructura del encabezado de Microsoft. Ponen std::next
en <xutility>
que se comparte entre <string>
y <iterator>
Nota: _FwdIt
y _Off
son parte del espacio de nombres de implementación. No uses guiones bajos a ti mismo.
Me ha mordido gravemente el siguiente código, en el que he perdido muchas horas de un tiempo precioso.
#include<string>
int next(std::string param){
return 0;
}
void foo(){
next(std::string{ "abc" });
}
Esto produce el siguiente error del compilador (en Visual Studio 2013):
1>------ Build started: Project: sandbox, Configuration: Debug Win32 ------
1> test.cpp
1>c:/program files (x86)/microsoft visual studio 12.0/vc/include/xutility(371): error C2039: ''iterator_category'' : is not a member of ''std::basic_string<char,std::char_traits<char>,std::allocator<char>>''
1> c:/users/ray/dropbox/programming/c++/sandbox/test.cpp(8) : see reference to class template instantiation ''std::iterator_traits<std::basic_string<char,std::char_traits<char>,std::allocator<char>>>'' being compiled
1>c:/program files (x86)/microsoft visual studio 12.0/vc/include/xutility(371): error C2146: syntax error : missing '';'' before identifier ''iterator_category''
1>c:/program files (x86)/microsoft visual studio 12.0/vc/include/xutility(371): error C4430: missing type specifier - int assumed. Note: C++ does not support default-int
1>c:/program files (x86)/microsoft visual studio 12.0/vc/include/xutility(371): error C2602: ''std::iterator_traits<std::basic_string<char,std::char_traits<char>,std::allocator<char>>>::iterator_category'' is not a member of a base class of ''std::iterator_traits<std::basic_string<char,std::char_traits<char>,std::allocator<char>>>''
1> c:/program files (x86)/microsoft visual studio 12.0/vc/include/xutility(371) : see declaration of ''std::iterator_traits<std::basic_string<char,std::char_traits<char>,std::allocator<char>>>::iterator_category''
1>c:/program files (x86)/microsoft visual studio 12.0/vc/include/xutility(371): error C2868: ''std::iterator_traits<std::basic_string<char,std::char_traits<char>,std::allocator<char>>>::iterator_category'' : illegal syntax for using-declaration; expected qualified-name
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
Más tarde descubrí que si cambio el nombre de mi función de next()
a otra cosa, todo está bien. Para mí, esto indica que hay un conflicto de nombre, específicamente del nombre next
. Me parece extraño porque no usé nada como using namespace std
. Por lo que sé, la next
no es una palabra clave C ++ incorporada (¿verdad?). Miré hacia arriba here , pero está std::next
y como dije no using namespace std
. Entonces, ¿cómo sucedió este conflicto? ¿Cómo puedo prevenir cosas similares en el futuro? ¿Qué otros nombres podrían causar un conflicto como este?
El compilador encontró la función estándar std::next
debido a la llamada búsqueda dependiente del argumento porque el argumento usado en la llamada - std :: string - está declarado en el espacio de nombres std
.
En realidad, std::next()
es una función definida en <iterator>
que devuelve el siguiente iterador pasado a std::next()
. Su código se ejecuta en mi computadora con gcc-4.9.2. Más: here
El código que utilicé:
#include<string>
#include <iostream>
int next(std::string param){
std::cout<<param<<std::endl;
return 0;
}
void foo(){
next(std::string{ "abc" });
}
int main()
{
foo();
return 0;
}
También en ideone: http://ideone.com/QVxbO4
Hay varias cosas sucediendo aquí, interactuando de manera sutil.
En primer lugar, una llamada no calificada a next
con un argumento de tipo std::string
significa que, al igual que su propia función next
, la búsqueda de dependencia de argumentos (ADL) encuentra la plantilla de función estándar std::next
.
Después de que la búsqueda de nombres haya encontrado su ::next
y la biblioteca estándar std::next
, realiza una resolución de sobrecarga para ver cuál es la mejor para los argumentos con los que lo llamó.
La definición de std::next
ve así:
template <class ForwardIterator>
ForwardIterator next(ForwardIterator x,
typename std::iterator_traits<ForwardIterator>::difference_type n = 1);
Esto significa que cuando el compilador realiza una resolución de sobrecarga, sustituye el tipo std::string
en std::iterator_traits<std::string>
.
Antes de C ++ 14 iterator_traits no es compatible con SFINAE, lo que significa que no es válido instanciarlo con un tipo que no sea un iterador. std::string
no es un iterador, por lo que no es válido. La regla SFINAE no se aplica aquí, porque el error no está en el contexto inmediato y, por lo tanto, el uso de iterator_traits<T>::difference_type
para cualquier no iterador T
producirá un error grave, no un error de sustitución.
Su código debería funcionar correctamente en C ++ 14, o usar una implementación de biblioteca estándar diferente que ya proporcione un iterator_traits compatible con SFINAE, como la biblioteca de GCC. Creo que Microsoft también proporcionará un iterator_traits compatible con SFINAE para la próxima versión importante de Visual Studio.
Para hacer que su código funcione ahora, puede calificar la llamada al next
para que no se realice ADL:
::next(std::string{ "abc" });
Esto indica que se debe llamar al next
en el espacio de nombres global, en lugar de a cualquier otro next
que se pueda encontrar por búsqueda de nombre no calificado.