definición múltiple en el archivo de encabezado

Dado este ejemplo de código:

complex.h:

#ifndef COMPLEX_H #define COMPLEX_H #include  class Complex { public: Complex(float Real, float Imaginary); float real() const { return m_Real; }; private: friend std::ostream& operator<<(std::ostream& o, const Complex& Cplx); float m_Real; float m_Imaginary; }; std::ostream& operator<<(std::ostream& o, const Complex& Cplx) { return o << Cplx.m_Real << " i" << Cplx.m_Imaginary; } #endif // COMPLEX_H 

complex.cpp:

 #include "complex.h" Complex::Complex(float Real, float Imaginary) { m_Real = Real; m_Imaginary = Imaginary; } 

main.cpp:

 #include "complex.h" #include  int main() { Complex Foo(3.4, 4.5); std::cout << Foo << "\n"; return 0; } 

Al comstackr este código, aparece el siguiente error:

 multiple definition of operator<<(std::ostream&, Complex const&) 

Descubrí que hacer esta función en inline resuelve el problema, pero no entiendo por qué. ¿Por qué el comstackdor se queja de la definición múltiple? Mi archivo de encabezado está protegido (con #define COMPLEX_H ).

Y, si se queja de la función del operator<< , ¿por qué no se queja de la función public real() , que también está definida en el encabezado?

¿Y hay otra solución además de usar la palabra clave en inline ?

El problema es que la siguiente pieza de código es una definición, no una statement:

 std::ostream& operator<<(std::ostream& o, const Complex& Cplx) { return o << Cplx.m_Real << " i" << Cplx.m_Imaginary; } 

Puede marcar la función anterior y hacerla "en línea" para que múltiples unidades de traducción puedan definirla:

 inline std::ostream& operator<<(std::ostream& o, const Complex& Cplx) { return o << Cplx.m_Real << " i" << Cplx.m_Imaginary; } 

O simplemente puede mover la definición original de la función al archivo fuente "complex.cpp".

El comstackdor no se queja de "real ()" porque está implícitamente en línea (cualquier función miembro cuyo cuerpo se da en la statement de clase se interpreta como si hubiera sido declarada "en línea"). Las protecciones del preprocesador evitan que su encabezado se incluya más de una vez en una sola unidad de traducción (archivo fuente "* .cpp"). Sin embargo, ambas unidades de traducción ven el mismo archivo de encabezado. Básicamente, el comstackdor comstack "main.cpp" para "main.o" (incluidas las definiciones dadas en los encabezados incluidos por "main.cpp"), y el comstackdor comstack "complex.cpp" por separado a "complex.o" (incluidas las definiciones dadas en los encabezados incluidos por "complejo .cpp "). Luego el enlazador combina" main.o "y" complex.o "en un solo archivo binario, es en este punto que el enlazador encuentra dos definiciones para una función del mismo nombre. señalar que el enlazador intenta resolver referencias externas (por ejemplo, "main.o" hace referencia a "Complex :: Complex" pero no tiene una definición para esa función ... el enlazador ubica la definición de "complex.o", y resuelve esa referencia).

¿Y hay otra solución como usar la palabra clave en inline ?

Sí hay. Aparte de definir el método dentro del archivo de implementación complex.cpp como lo mencionaron otros, también puede poner la definición en un espacio de nombre sin nombre.

 namespace { std::ostream& operator<<(std::ostream& o, const Complex& Cplx) { return o << Cplx.m_Real << " i" << Cplx.m_Imaginary; } } 

En la práctica, esto creará un espacio de nombre único para cada unidad de comstackción. De esa forma, evitas los conflictos de nombres. Sin embargo, los nombres todavía se exportan de la unidad de comstackción pero son inútiles (ya que los nombres son desconocidos).

Poner la definición dentro del archivo de implementación a menudo es una mejor solución. Sin embargo, para las plantillas de clase no puede hacer eso ya que los comstackdores de C ++ no admiten la creación de instancias de plantillas en una unidad de comstackción diferente a la que se definieron. En esos casos, debe usar un espacio de nombres en inline o sin nombre.

Mueva la implementación a complex.cpp

Ahora mismo, después de incluir esta implementación de archivo, se está comstackndo en cada archivo. Más tarde durante la vinculación hay un conflicto obvio debido a las implementaciones duplicadas.

:: real () no se informa porque está en línea implícitamente (implementación dentro de la definición de clase)

Estaba teniendo este problema, incluso después de que mi fuente y el archivo de encabezado eran correctos.

Resultó que Eclipse estaba usando artefactos obsoletos de una comstackción anterior (fallida).

Para solucionarlo, use Project > Clean luego reconstruir.