#include <iostream>
#include <cmath>
/* Intentionally incorrect abs() which seems to override std::abs() */
int abs(int a) {
return a > 0? -a : a;
}
int main() {
int a = abs(-5);
int b = std::abs(-5);
std::cout<< a << std::endl << b << std::endl;
return 0;
}
Esperaba que la salida fuera -5
y 5
, pero la salida es -5
y -5
.
Me pregunto por qué sucederá este caso.
¿Tiene algo que ver con el uso de std
o qué?
abs
es incorrecta.abs
efectos rotosstd::abs()
.5
y5
con clang,-5
y-5
con gcc.return 0
; eso hubiera evitado que la gente pensara que implementó la función de manera incorrecta involuntariamente y habría aclarado el comportamiento deseado y real.Respuestas:
La especificación del lenguaje permite que las implementaciones se implementen
<cmath>
declarando (y definiendo) las funciones estándar en el espacio de nombres global y luego llevándolas al espaciostd
de nombres por medio de declaraciones de uso. No se especifica si se utiliza este enfoque.Aparentemente, se trata de una de las implementaciones que decidió seguir este enfoque (por ejemplo, GCC). Es decir, su implementación proporciona
::abs
, mientras questd::abs
simplemente "se refiere" a::abs
.Una pregunta que permanece en este caso es por qué, además del estándar
::abs
, pudo declarar el suyo::abs
, es decir, por qué no hay error de definición múltiple. Esto podría deberse a otra característica proporcionada por algunas implementaciones (por ejemplo, GCC): declaran funciones estándar como los llamados símbolos débiles , lo que le permite "reemplazarlos" con sus propias definiciones.Estos dos factores juntos crean el efecto que observa: el reemplazo de símbolo débil de
::abs
también da como resultado el reemplazo destd::abs
. Lo bien que esto concuerda con el estándar del lenguaje es una historia diferente ... En cualquier caso, no confíe en este comportamiento, no está garantizado por el lenguaje.En GCC, este comportamiento se puede reproducir con el siguiente ejemplo minimalista. Un archivo fuente
Otro archivo fuente
En este caso, también observará que la nueva definición de
::foo
("Goodbye!"
) en el segundo archivo fuente también afecta el comportamiento deN::foo
. Ambas llamadas saldrán"Goodbye!"
. Y si elimina la definición de::foo
del segundo archivo fuente, ambas llamadas se enviarán a la definición::foo
y salida "original""Hello!"
.El permiso otorgado por el 20.5.1.2/4 anterior está ahí para simplificar la implementación de
<cmath>
. Se permite que las implementaciones incluyan simplemente el estilo C<math.h>
, luego redeclaren las funcionesstd
y agreguen algunas adiciones y ajustes específicos de C ++. Si la explicación anterior describe correctamente la mecánica interna del problema, entonces una gran parte depende de la posibilidad de reemplazar los símbolos débiles por las versiones de estilo C de las funciones.Tenga en cuenta que si simplemente reemplazamos globalmente
int
condouble
en el programa anterior, el código (en GCC) se comportará "como se esperaba": se generará-5 5
. Esto sucede porque la biblioteca estándar de C no tieneabs(double)
función. Al declarar nuestroabs(double)
, no reemplazamos nada.Pero si después de cambiar de
int
condouble
también cambiamos deabs
afabs
, el comportamiento extraño original reaparecerá en todo su esplendor (salida-5 -5
).Esto es consistente con la explicación anterior.
fuente
using ::abs;
parecido para el, porusing ::asin;
lo que puede anular la declaración, otro punto a mencionar es que las funciones de espacio de nombres definidas en std no se declaran para int sino para double , float#include<cmath>
en mi código, obtuve la misma respuestaabs
se puede declarar<cstdlib>
, que podría incluirse implícitamente mediante<iostream>
. Intente eliminar el suyoabs
y ver si aún se compila.Su código provoca un comportamiento indefinido.
C ++ 17 [nombres.extern] / 4:
Por lo tanto, no puede crear una función con el mismo prototipo que la función de la biblioteca C estándar
int abs(int);
. Independientemente de qué encabezados incluya realmente o si esos encabezados también colocan los nombres de las bibliotecas de C en el espacio de nombres global.Sin embargo, podría sobrecargarse
abs
si proporciona diferentes tipos de parámetros.fuente