¿Por qué mi log en el espacio de nombres estándar?

En el siguiente código, defino una función de log trivial. En main bash no llamarlo; Llamo a std::log . Sin embargo, mi propio log se llama; y veo “log!” en la pantalla. ¿Alguien sabe por qué? Uso G ++ 4.7 y clang ++ 3.2.

 #include  #include  double log(const double x) { std::cout << "log!\n"; return x; } int main(int argc, char *argv[]) { std::log(3.14); return 0; } 

Norma C ++ 17.6.1.2, párrafo 4 (énfasis mío):

Excepto como se señala en las Cláusulas 18 a 30 y Anexo D, el contenido de cada encabezado cname debe ser el mismo que el del encabezado name.h correspondiente, como se especifica en la biblioteca C Standard (1.2) o en el C Unicode TR, según corresponda como si fuera por inclusión En la biblioteca estándar de C ++, sin embargo, las declaraciones (a excepción de los nombres que se definen como macros en C) se encuentran dentro del scope del espacio de nombres (3.3.6) del espacio de nombres std . No se especifica si estos nombres se declaran primero dentro del ámbito del espacio de nombres global y luego se inyectan en el espacio de nombres std mediante declaraciones de uso explícitas (7.3.3).

g ++ lo hace de la última manera, por lo que algunos de los mismos archivos de cabecera se pueden reutilizar para C y C ++. Entonces, g ++ puede declarar y definir el double log(double) en el espacio de nombres global.

Sección 17.6.4.3.3, párrafos 3 y 4:

Cada nombre de la biblioteca Standard C declarada con un enlace externo está reservado a la implementación para su uso como un nombre con un enlace extern "C" , tanto en el espacio de nombres std como en el espacio de nombres global.

Cada firma de función de la biblioteca Standard C declarada con enlaces externos está reservada a la implementación para su uso como firma de función con enlaces extern "C" y extern "C++" , o como un nombre de ámbito de espacio de nombres en el espacio de nombres global.

Y arriba en la parte superior de la Sección 17.6.4.3, párrafo 2:

Si un progtwig declara o define un nombre en un contexto donde está reservado, a menos que esté explícitamente permitido por esta Cláusula, su comportamiento no está definido.

Usted, por otro lado, no puede declarar ni definir ::log de ninguna manera.

Es una lástima que la cadena de herramientas g ++ no te proporcione ningún mensaje de error.

Lo que sucede, espero, es que std::log simplemente delegue a ::log . Desafortunadamente, ::log solo proporciona una sobrecarga float , y usted amablemente proporciona una double sobrecarga, haciendo que la suya sea una mejor coincidencia. Pero todavía no veo cómo se considera en el conjunto de sobrecarga.

En el cmath libstdc ++ verás esto:

 using ::log; 

Por lo tanto, está incorporando las funciones math.h del espacio de nombres global en std . Lamentablemente, está suministrando una implementación para el double log(double) , por lo que el vinculador no usará el de math lib. Así que definitivamente es un error en libstdc ++ .

EDIT: afirmo que es un error en libstdc ++ porque std::log no debería sufrir interferencias con la biblioteca C cuando explícitamente está pidiendo las versiones std:: . Por supuesto, esta forma de anular las funciones estándar de la biblioteca es una antigua “característica” del lenguaje C.

EDIT 2: descubrí que el estándar en realidad no prohíbe traer nombres del espacio de nombres global a std . Entonces no es un error después de todo, solo una consecuencia de los detalles de implementación.

En C ++, el comstackdor puede implementar libremente la biblioteca C en el espacio de nombres global y delegar en ella (esta es la implementación definida).

17.6.1.2.4 Con excepción de lo señalado en las Cláusulas 18 a 30 y el Anexo D, el contenido de cada encabezado cname debe ser el mismo que el nombre del encabezado correspondiente.h, según se especifica en la biblioteca estándar C (1.2) o en la C Unicode TR, según corresponda, como si fuera por inclusión. Sin embargo, en la biblioteca estándar de C ++, las declaraciones (excepto los nombres que se definen como macros en C) se encuentran dentro del ámbito de espacio de nombres (3.3.6) del espacio de nombres std. No se especifica si estos nombres se declaran por primera vez dentro del ámbito del espacio de nombres global y luego se inyectan en el espacio de nombres std mediante declaraciones de uso explícitas (7.3.3).

En general, evitaría hacer una función con la misma firma que una de las bibliotecas estándar de C. El estándar de C ++ ciertamente le da a los comstackdores la libertad de usar estas firmas si así lo desea, lo que significa que puede estar luchando contra su comstackdor si intenta usar las mismas firmas. Por lo tanto, obtienes resultados extraños.

Sin embargo, esperaría un error o advertencia del enlazador, y creo que puede valer la pena informar esto.

[editar]

Guau, ninja.

Porque lo has anulado en el espacio de nombres global. El uso de un espacio de nombres evita ese peligro si no desea pasar a un lenguaje más seguro y más limpio como Nim, por ejemplo.

Uso adecuado de la demo del espacio de nombres :

 #include  #include  // Uses ::log, which would be the log() here if it were not in a namespace, see http://stackoverflow.com/questions/11892976/why-is-my-log-in-the-std-namespace // Silently overrides std::log //double log(double d) { return 420; } namespace uniquename { using namespace std; // So we don't have to waste space on std:: when not needed. double log(double d) { return 42; } int main() { cout < < "Our log: " << log(4.2) << endl; cout << "Standard log: " << std::log(4.2); return 0; } } // Global wrapper for our contained code. int main() { return uniquename::main(); } 

Salida:

 Our log: 42 Standard log: 1.43508