2012-08-09 20 views
64

En el siguiente código, defino una función trivial log. En main intento no para llamarlo; Llamo al std::log. Sin embargo, se llama mi propio log; y veo "log!" en la pantalla. ¿Alguien sabe por qué? Uso G ++ 4.7 y clang ++ 3.2.¿Por qué mi log en el espacio de nombres estándar?

#include <iostream> 
#include <cmath> 

double log(const double x) { std::cout << "log!\n"; return x; } 

int main(int argc, char *argv[]) 
{ 
    std::log(3.14); 
    return 0; 
} 
+10

suena como un error grave compilador ... – MFH

+0

puedo reproducir este en g ++ 4.6 bajo MacPorts. Sin embargo, no ocurre en g ++ 4.2 o 4.4. – carlosdc

+1

http://codepad.org/Uwhgrv7q http://codepad.org/z07Ctfyn Frome estos dos diría que la función std :: log() llama a log().pero luego debería generar un error/advertencia de que su archivo redefine el registro o algo así como t – Gir

Respuesta

53

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

Excepto como se indica en las cláusulas 18 a través de 30 y el Anexo D, el contenido de cada cabecera cname será la misma que la de la cabecera correspondiente name.h, tal como 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 primero dentro del ámbito de espacio de nombres global y luego se inyectan en el espacio de nombres std mediante declaraciones de uso (7.3.3).

g ++ lo hace este último camino para que algunos de los mismos archivos de cabecera pueden ser reutilizados para C y C++. Entonces g ++ puede declarar y definir double log(double) en el espacio de nombres global.

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

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

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

Y en la parte superior de la sección 17.6.4.3 párrafo 2:

Si un programa declara o define un nombre en un contexto en el que se reserva, con excepción de lo permitido expresamente por esta Cláusula, su comportamiento no está definido.

Usted, por el contrario, puede no declarar o definir ::log de ninguna manera.

Es una lástima que la cadena de herramientas g ++ no le dé ningún mensaje de error.

+6

+1: Esta es la mejor respuesta. –

8

¿Qué ocurre, espero, es que simplemente std::log delegados a ::log. Lamentablemente, ::log solo proporciona una sobrecarga de float, y usted amablemente proporciona una sobrecarga de double, haciendo que la suya sea una mejor opción. Pero todavía no veo cómo se considera en el conjunto de sobrecarga.

+0

'' es necesario para proporcionar 'log (double)'. – aschepler

+1

Esperaba lo mismo en MSVC10 y, de hecho, sí obtuve un error de símbolo duplicado en el enlace, pero solo después de 3 o 4 reconstrucciones. –

+0

@aschepler: '' es necesario para proporcionar 'double std :: log (double)' (estoy haciendo que el foco en el espacio de nombres sea un gran problema, porque es ahora). El estándar requiere que estén en el espacio de nombres ':: std', y cada implementación es libre de definirlos en el espacio de nombres global si así lo desean. – Cornstalks

8

En libstdC++ 's cmath verá esto:

using ::log; 

Por lo que es la incorporación de las funciones math.h desde el espacio de nombres global en std. Lamentablemente, está suministrando una implementación para double log(double), por lo que el vinculador no utilizará la de math lib. Definitivamente un error en libstdC++ .

EDIT: reclamo 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 no prohíbe traer nombres del espacio de nombres global al std. Entonces no es un error después de todo, solo una consecuencia de los detalles de implementación.

+0

La declaración 'using' solo debería importar las declaraciones que están visibles en ese punto, creo, lo que significaría que una función declarada posteriormente no debería importarse de esta manera. – bames53

+0

He editado mi respuesta, el estándar permite este tipo de implementación, primero declara las funciones math.h, luego trae las versiones 'double' a' std'. – DanielKO

6

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

17.6.1.2.4 Excepto como se indica en las cláusulas 18 a través de 30 y el Anexo D, el contenido de cada cname cabecera serán los mismos que el de la name.h cabecera correspondiente, tal como se especifica en la norma C biblioteca (1.2) o 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) están dentro del ámbito de espacio de nombres (3.3.6) del espacio de nombres std. Es sin especificar si estos nombres se declaran por primera vez dentro del ámbito de espacio de nombres global y luego se inyectan en el espacio de nombre estándar 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 compiladores la libertad de usar estas firmas si así lo desea, lo que significa que puede estar luchando contra su compilador 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]

Wow, ninja'd.

+0

Mejor suerte la próxima vez :) – user2023370

0

Porque lo ha 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 limpio como Nim, por ejemplo.

Proper use of namespace demo:

#include <iostream> 
#include <cmath> // 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 
Cuestiones relacionadas