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 espacio std
de nombres por medio de declaraciones de uso. No se especifica si se utiliza este enfoque.
20.5.1.2 Encabezados
4 [...] En la biblioteca estándar de C ++, sin embargo, las declaraciones (excepto los nombres que se definen como macros en C) están dentro del alcance del espacio de nombres (6.3.6) del espacio de nombres std
. No se especifica si estos nombres (incluidas las sobrecargas agregadas en las Cláusulas 21 a 33 y el Anexo D) se declaran primero dentro del alcance del espacio de nombres global y luego se inyectan en el espacio std
de nombres mediante declaraciones de uso explícitas (10.3.3).
Aparentemente, se trata de una de las implementaciones que decidió seguir este enfoque (por ejemplo, GCC). Es decir, su implementación proporciona ::abs
, mientras que std::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
#include <iostream>
void foo() __attribute__((weak));
void foo() { std::cout << "Hello!" << std::endl; }
Otro archivo fuente
#include <iostream>
void foo();
namespace N { using ::foo; }
void foo() { std::cout << "Goodbye!" << std::endl; }
int main()
{
foo();
N::foo();
}
En este caso, también observará que la nueva definición de ::foo
( "Goodbye!"
) en el segundo archivo fuente también afecta el comportamiento de N::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 funciones std
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
con double
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 tiene abs(double)
función. Al declarar nuestroabs(double)
, no reemplazamos nada.
Pero si después de cambiar de int
con double
también cambiamos de abs
a fabs
, el comportamiento extraño original reaparecerá en todo su esplendor (salida-5 -5
).
Esto es consistente con la explicación anterior.
abs
es incorrecta.