libreria c++ c++11 c++14 nan cmath

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 / Open int 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ón isnan 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 un bool isnan(double); en el espacio de nombres global (a menos que libc math.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?

[depr.c.headers] :

... 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 nombres std 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 nombres std . 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 nombres std . - 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).