c++ - redimensionar - std:: vector methods
¿Puede una declaración afectar el espacio de nombres estándar? (2)
#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 fuera
-5
y
-5
.
Me pregunto por qué va a pasar este caso.
¿Tiene algo que ver con el uso de
std
o qué?
La especificación del lenguaje
allows
implementaciones implementen
<cmath>
declarando (y definiendo) las funciones estándar en
el
espacio de nombres
global
y luego llevándolas al espacio de nombres
std
por medio de declaraciones de uso.
No se especifica si este enfoque se utiliza
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 nombresstd
. 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 de nombresstd
mediante declaraciones de uso explícitas (10.3.3).
Aparentemente, está tratando con 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 queda en este caso es por qué, además del estándar
::abs
, pudo declarar su propio
::abs
, es decir, por qué no hay un error de definición múltiple.
Esto puede deberse a otra característica proporcionada por algunas implementaciones (por ejemplo, GCC): declaran funciones estándar como los denominados
símbolos débiles
, lo que le permite "reemplazarlos" con sus propias definiciones.
Estos dos factores juntos crean el efecto que usted observa: el reemplazo con símbolo débil de
::abs
también resulta en el reemplazo de
std::abs
.
En qué medida está de acuerdo con el estándar de idioma es una historia diferente ... En cualquier caso, no confíe en este comportamiento, ya que no está garantizado por el idioma.
En GCC, este comportamiento se puede reproducir mediante 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 de origen también afecta el comportamiento de
N::foo
.
Ambas llamadas emitirán
"Goodbye!"
.
Y si elimina la definición de
::foo
del segundo archivo de origen, ambas llamadas se enviarán a la definición "original" de
::foo
y la salida
"Hello!"
.
El permiso dado por el anterior 20.5.1.2/4 está ahí para simplificar la implementación de
<cmath>
.
Las implementaciones pueden incluir simplemente el estilo C
<math.h>
, luego volver a declarar las funciones en
std
y agregar algunas adiciones y ajustes específicos de C ++.
Si la explicación anterior describe adecuadamente la mecánica interna del problema, entonces una parte importante depende de la capacidad de reemplazo de los símbolos débiles para
las versiones
de
estilo C
de las funciones.
Tenga en cuenta que si simplemente reemplazamos globalmente el
int
con el
double
en el programa anterior, el código (bajo GCC) se comportará "como se esperaba" - generará
-5 5
.
Esto sucede porque la biblioteca estándar de C no tiene función
abs(double)
.
Al declarar nuestros propios
abs(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 toda su gloria (salida
-5 -5
).
Esto es consistente con la explicación anterior.
Tu código provoca un comportamiento indefinido.
C ++ 17 [extern.names] / 4:
Cada firma de función de la biblioteca estándar de C declarada con enlace externo se reserva a la implementación para su uso como una firma de función con enlace externo "C" y externo "C ++", o como un nombre de ámbito de espacio de nombres en el espacio de nombres global.
Por lo tanto, no puede crear una función con el mismo prototipo que la función de biblioteca de C estándar
int abs(int);
.
Independientemente de los encabezados que realmente incluya o si esos encabezados también colocan los nombres de la biblioteca C en el espacio de nombres global.
Sin embargo, se podría sobrecargar los
abs
si proporciona diferentes tipos de parámetros.