2011-07-27 10 views
8

Pasé días en un problema extraño y finalmente descubrí que había dos funciones inline de la misma firma en el proyecto y que causaron el problema. Para simplificar la situación aquí es un ejemplo: archivo de dos CPP:¿Qué ocurre si redefinimos una función en línea?

a.cpp

#include <iostream> 

void b(); 

inline void echo() 
{ 
    std::cout << 0 << std::endl; 
} 

int main() 
{ 
    echo(); 
    b(); 
    return 0; 
} 

y b.cpp

#include <iostream> 

inline void echo() 
{ 
    std::cout << 1 << std::endl; 
} 

void b() 
{ 
    echo(); 
} 

Tenga en cuenta que inline funciones echo tienen la misma firma pero diferente implementos. Compilar y ejecutar

g++ a.cpp b.cpp -o a.out && ./a.out 

O como esta

g++ a.cpp -c 
g++ b.cpp -c 
g++ a.o b.o -o a.out 
./a.out 

Imprime 0 0. (Yo estaba usando g ++ 4.6.1 para eso, y he probado con sonido metálico ++ 2.9, mismo resultado)

Eso no sucederá si encender la optimización, como

g++ -O3 a.cpp b.cpp -o a.out && ./a.out 

Es 0 1 este momento.

Mi pregunta es, sin importar el resultado o cómo se realiza la compilación, no hay ningún error o incluso advertencia sobre que he definido inline funciones varias veces. ¿Qué sucede con el compilador y el enlazador en este tipo de situación?

EDIT:

Tome un vistazo a los símbolos en el archivo de objeto

nm a.o b.o | c++filt 

Ambos archivos tienen el registro echo(). Entonces, creo que el problema ocurre en el momento del enlace. ¿Podría decirse que el enlazador selecciona aleatoriamente una implementación y descarta todas las demás?

+0

¿Ha probado una mayor verbosidad de advertencia (-Wall, etc.)? – schnaader

+0

Acabo de probar '-Wall -Wextra', todavía sin aviso. – neuront

Respuesta

5

El compilador no es necesario para diagnosticar esta infracción de ODR, y no es trivial. La palabra clave inline significa que diferentes unidades de traducción pueden tener el mismo símbolo, por lo que el compilador lo marca como débil. El caso de uso básico es una función definida en línea en un encabezado: todas las unidades de traducción que incluyen el encabezado tendrán la definición, y está perfectamente bien. El compilador solo necesita descartar todas las definiciones menos una y usar esa definición en todas partes.

La detección de si las diferentes definiciones son coincidencias exactas es un problema complejo.El enlazador tendría que analizar la implementación binaria generada y determinar si los dos códigos binarios se relacionan con el mismo código fuente o no. La mayoría de los compiladores no tienen soporte para determinar esto.

A partir de su problema particular, no puedo saber posiblemente la razón que llevó a las dos funciones están marcadas en línea, pero un error común es el uso de la palabra clave inline para representar optimizar el en lugar de no se quejan de repeticiones en tiempo de enlace. La palabra clave inline tiene sentido en los encabezados, pero no tanto en los archivos cpp. En los archivos cpp, si desea factorizar un fragmento de código en una función auxiliar, esa función debe marcarse static o definirse dentro de un espacio de nombre sin nombre. Si la función es static, entonces el compilador sabe que todos los usos de esa función están dentro de su unidad de traducción, y tiene un mayor conocimiento para decidir si desea alinear o no la llamada de función (tenga en cuenta que puede alinear incluso si no No se lo digas, de la misma manera que puede decidir no alinearse incluso si lo dices).

12

En el C++ estándar se afirma que todas las definiciones de una función en línea serán los mismos, pero no de diagnóstico se requiere. Es decir, su programa no es un programa C++ válido, pero una implementación tiene el derecho de no detectar ese error.

Ver la Cláusula 3.2.5. Es demasiado largo para publicar aquí.

6

Este mismo caso (dos funciones en línea con el mismo nombre y las mismas firmas que tienen implementaciones diferentes) leads to undefined behavior. El compilador no está obligado a diagnosticarlo, aunque podría intentarlo.

Cuestiones relacionadas