libreria - pow c++
<cmath> oculta el isnan en<math.h> en C++ 14/C++ 11? (3)
Tengo aquí una pequeña aplicación de prueba que usa isnan
de <math.h>
:
#include <iostream>
#include <math.h>
int main()
{
double d = NAN;
std::cout << isnan(d) << ''/n'';
return 0;
}
Construye y ejecuta bajo 3 estándares diferentes:
$ g++ -std=c++98 main.cpp; ./a.out 1 $ g++ -std=c++11 main.cpp; ./a.out 1 $ g++ -std=c++14 main.cpp; ./a.out 1
Ahora también incluimos <cmath>
y probamos con isnan
y std::isnan
:
#include <iostream>
#include <cmath>
#include <math.h>
int main()
{
double d = NAN;
std::cout << std::isnan(d) << ''/n'';
std::cout << isnan(d) << ''/n'';
return 0;
}
Construir y ejecutar:
C ++ 98 funciona
$ g++ -std=c++98 main.cpp; ./a.out 1 1
C ++ 11 y C ++ 14 no, no se encuentra isnan
.
$ g++ -std=c++11 main.cpp main.cpp: In function ‘int main()’: main.cpp:10:25: error: ‘isnan’ was not declared in this scope std::cout << isnan(d) << ''/n''; ^ main.cpp:10:25: note: suggested alternative: In file included from main.cpp:3:0: /usr/include/c++/5/cmath:641:5: note: ‘std::isnan’ isnan(_Tp __x) ^ $ g++ -std=c++14 main.cpp main.cpp: In function ‘int main()’: main.cpp:10:25: error: ‘isnan’ was not declared in this scope std::cout << isnan(d) << ''/n''; ^ main.cpp:10:25: note: suggested alternative: In file included from main.cpp:3:0: /usr/include/c++/5/cmath:641:5: note: ‘std::isnan’ isnan(_Tp __x) ^
Tenga en cuenta que el orden de inclusión no es importante. Si <cmath>
antes de <math.h>
o después, el resultado es el mismo.
Preguntas
- ¿Por qué se fue
isnan
? - Sin tener que volver atrás y cambiar el código antiguo para compilar bajo el nuevo estándar, ¿hay alguna forma de solucionar esto?
Muchas de las funciones dentro de math.h
son en realidad macros. Dado que esto no es idiomático c ++, el encabezado cmath
contiene el siguiente código:
...
#undef isinf
#undef isnan
#undef isnormal
...
E implementa entonces todas esas macros indefinidas como función en el namespace std
. Esto es al menos cierto para gcc 6.1.1. Es por eso que tu compilador no puede encontrar a isnan
.
Resumiendo brevemente los puntos pertinentes, principalmente de la excelente publicación de blog de Jonathan Wakely :
- glibc <2.23 de
math.h
declara el X / Openint isnan(double);
eso es incompatible con la versión C99 / C ++ 11 (bool isnan(double);
). -
math.h
glibc 2.23 corrige esto al no declarar la funciónisnan
en C ++ 11 o posterior. - Todos ellos siguen definiendo una macro
isnan
.#include <cmath>
esa macro como lo requiere el estándar C ++. - El libstdc ++ de GCC 6 proporciona su propio encabezado
math.h
especial que declara unbool isnan(double);
en el espacio de nombres global (a menos que libcmath.h
declare la firma obsoleta) y también nukes las macros como lo requiere el estándar. - Antes de GCC 6,
#include <math.h>
simplemente incluía el encabezado de su libc, por lo que la macro no está nuked. -
#include <cmath>
siempre#include <cmath>
las macros.
Resultado neto, en modo C ++ 11:
glibc < 2.23, GCC < 6: <math.h> uses the macro; <cmath> uses obsolete signature
glibc >= 2.23, GCC < 6: <math.h> uses the macro; <cmath> results in error
glibc < 2.23, GCC >= 6: <math.h> and <cmath> use obsolete signature
glibc >= 2.23, GCC >= 6: <math.h> and <cmath> use standard signature
Si miras dentro de <cmath>
de GCC, tiene esto:
. . .
#include <math.h>
. . .
#undef isnan
Es por eso que el orden no importa: siempre que #include <cmath>
, <math.h>
se incluya automáticamente y su contenido esté (parcialmente) nuked.
Intentar incluirlo de nuevo no tendrá efecto debido a #ifndef _MATH_H
.
Ahora, ¿qué tiene que decir el estándar sobre este comportamiento?
... cada encabezado C, cada uno de los cuales tiene un nombre del formulario
name.h
, se comporta como si cada nombre colocado en el espacio de nombres de la biblioteca estándar por el encabezado correspondiente del nombre c se colocara dentro del ámbito del espacio de nombres global. No se especifica si estos nombres se declaran o se definen primero dentro del ámbito del espacio de nombres ([basic.scope.namespace]) del espacio de nombresstd
y luego se inyectan en el ámbito del espacio de nombres global mediante declaraciones-declarativas explícitas ([namespace.udecl]).[Ejemplo: el encabezado
<cstdlib>
seguramente proporciona sus declaraciones y definiciones dentro del espacio de nombresstd
. También puede proporcionar estos nombres dentro del espacio de nombres global. El encabezado<stdlib.h>
seguramente proporciona las mismas declaraciones y definiciones dentro del espacio de nombres global, como en el Estándar C. También puede proporcionar estos nombres dentro del espacio de nombresstd
. - ejemplo final]
Entonces está bien que <cmath>
no proporcione isnan
en el espacio de nombres global.
Pero es un área gris lo que debería suceder cuando ambas se incluyen en una unidad de compilación, aunque se podría argumentar que la declaración anterior implica que ambas versiones deben interoperar, en cuyo caso sería un error en GCC / libstdc ++ (algunas versiones).