2011-08-06 15 views
8

tengo un archivo llamado "SimpleFunctions.h" se define de la siguiente manera:función ya definida error en C++

#ifndef SIMPLEFUNCTIONS_H 
#define SIMPLEFUNCTIONS_H 

namespace my_namespace { 

double round(double r) { return (r > 0.0) ? floor(r + 0.5) : ceil(r - 0.5); } 
float round(float r) { return round((double)r); } 

} 

#endif // SIMPLEFUNCTIONS_H 

Este archivo se incluyó previamente en un solo archivo y se trabajaba muy bien.

Ahora hoy lo he incluido en un segundo archivo y ya no funciona. En el momento del enlace, me dice que la función ya está definida en "firstfile.obj".

Sin embargo, dado que estoy usando guardias de inclusión, esperaría que las funciones se definan solo una vez, ¿me falta algo?

Respuesta

17

Por defecto, estas funciones tienen un enlace externo. Eso significa que cada unidad de traducción tiene funciones llamadas doble ronda (doble r) y ronda flotante (flotación r), lo que provoca una colisión de nombre en el tiempo del enlace.

Algunas soluciones posibles son:

  1. declarar las funciones como estática, lo que implica la vinculación interna
  2. Inline las funciones
  3. Mueva la aplicación de la cabecera y en una corriente alterna Archivo/C++

Lea más aquí: What is external linkage and internal linkage?

Por cierto, incluya las protecciones que protegen una sola unidad de traducción para que no incluya un archivo de encabezado varias veces. Ese es un problema diferente de lo que estás viendo aquí.

+0

Estás en lo correcto. Es por eso que debes evitar implementar funciones dentro de los encabezados. –

+0

'inline' does _not_ implica vinculación interal. http://stackoverflow.com/questions/4957582/how-can-i-prove-that-inline-functions-default-to-internal-linkage –

+0

@Charles: mi error - He editado la respuesta – pepsi

2

uso 'inline'

inline double round(double r) { return (r > 0.0) ? floor(r + 0.5) : ceil(r - 0.5); } 
inline float round(float r) { return round((double)r); } 

El compilador no será necesariamente el código inline (aunque para este corto func fuere), pero el enlazador no es tratar como una función separada más.

Nota: incluir guardias para detener el mismo archivo de inclusión que se incluye más de una vez en el mismo archivo fuente (estrictamente hablando 'unidad de compilación') no impide que se incluya en archivos fuente separados que están vinculados entre sí. Es por eso que normalmente lo declara en un encabezado, pero define la función en un archivo c

+0

Eso funcionó, gracias. Sin embargo, tengo curiosidad: ¿por qué el compilador incluye el archivo dos veces aunque haya un protector de inclusión? –

+0

¡Me gustaría saberlo también! :) – Casey

+1

Es una unidad de traducción independiente: la macro define no abarca unidades de traducción (invocaciones de compilador). El vinculador ahora tiene 2 definiciones de ronda y errores de salida. 'static' también es correcto para resolver el problema. –

1

Una mejor manera de resolver el problema es a través de plantillas. Su código se compilará bien si se va a hacer algo a lo largo de las líneas de:

template <class T> 
T round (T r) { 
    return (r > 0.0) ? floor(r + 0.5) : ceil(r - 0.5); 
} 

su enlazador dejar de quejarse y que tendrá una función única para todas sus necesidades.

Esta solución se puede mejorar con características de tipo. Ver boost::is_floating_point y boost::enable_if

+0

Haces trampa, pero en el buen sentido. Funciona. ¡Incluso es la mejor solución para ciertos escenarios! Gracias. – javaLover

Cuestiones relacionadas