Desventajas de utilizar una función C++ estándar «global»: su contraparte en C se puede camuflar
Última actualización el 30 de septiembre de 2019
Una gran cantidad de funciones de C++ en la biblioteca estándar se extienden a partir de funciones de C estándar, como qsort()
, memcpy()
, etc. Entre estas funciones, muchas sobrecargaron a sus homólogos en C. Por ejemplo, abs()
en C++ está sobrecargado tanto para tipos integrales como de punto flotante, mientras que solo está definido para int
Cª. Sin embargo, estas funciones a menudo se utilizan indebidamente como funciones globales y producen resultados inesperados.
Las funciones de la biblioteca estándar de C++ generalmente se colocan en el espacio de nombres. std
, incluidos aquellos que están extendidos por sus homólogos C. Por ejemplo, la función C sqrt()
corresponde a std::sqrt()
en C++. La siguiente función my_sqrt_1
calcula la raíz cuadrada del parámetro de entrada x
:
#include <cmath>
float my_sqrt_1(float x) {
return std::sqrt(x);
}
Sin embargo, de vez en cuando, la gente pierde su std::
prefijo, crees que es demasiado redundante. Por lo tanto, el fragmento de código anterior a menudo se abrevia como (usando la «versión global» de sqrt()
)
#include <cmath>
// no "using namespace std;"
float my_sqrt_2(float x) {
return sqrt(x);
}
Si bien puede parecer un buen truco, es posible que no funcione como se esperaba. En C++, el comportamiento de llamar a una función estándar sin usar explícitamente el espacio de nombres std
depende de la implementación: un compilador puede interpretar la función como la función C++ o su contraparte C. En el ejemplo anterior, my_sqrt_2
podría posiblemente cualquiera
- él llamó
float std::sqrt(float)
conx
como entrada y devolver su valor de retorno (este es el caso de x64 msvc v19.22 como se probó en C Compiler Explorer, o - elenco
x
adouble
escribir, cosas endouble sqrt(double)
devolver el valor adouble
y devuélvalo (este es el caso de x86-64 gcc 9.2 y x86-64 clang 9.0.0 en C Compiler Explorer.
El segundo caso puede ocurrir porque el compilador interpretó sqrt()
como la función C sqrt()
que sólo está definido para double
. Si la función recién definida se llama repetidamente, esto puede causar una desventaja de rendimiento significativa. Lo que es peor, a veces incluso puede provocar resultados erróneos. Considere la siguiente función:
#include <cmath>
long double my_sqrt_3(long double x) {
return sqrt(x);
}
Si sqrt()
se interpreta como la C sqrt()
función, en un chip de CPU que la admita long double
tipo que es más preciso que double
Es probable que esta función cause una pérdida de precisión cada vez que se llama e incluso puede producir resultados ridículos cuando x
es lo suficientemente grande. En una CPU x86_64 reciente, usando gcc 8.3, el siguiente fragmento de código
#include <iostream>
int main() {
std::cout << my_sqrt_3(1e310L) << ' ' << std::sqrt(1e310L) << std::endl;
return 0;
}
salidas
inf 1e+155
¡La raíz cuadrada de un número finito es infinita! Esto es porque x
se lanza implícitamente por primera vez para double
en my_sqrt_3
que no es lo suficientemente preciso como para almacenar un número tan grande como 1e310
y por lo tanto convertido en inf
.
Conclusión: no utilice la «versión global» de las funciones estándar de C++; siempre usa eso en el std
espacio de nombres.