definizione multipla nel file di intestazione

Dato questo esempio di codice:

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; } 

Quando compilo questo codice, ottengo il seguente errore:

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

Ho scoperto che rendere questa funzione in inline risolve il problema, ma non capisco perché. Perché il compilatore si lamenta della definizione multipla? Il mio file di intestazione è protetto (con #define COMPLEX_H ).

E, lamentandosi della funzione operator<< , perché non lamentarsi della funzione public real() , che è definita anche nell’intestazione?

E c’è un’altra soluzione oltre all’utilizzo della parola chiave inline ?

Il problema è che la seguente parte di codice è una definizione, non una dichiarazione:

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

Puoi contrassegnare la funzione sopra e renderla "in linea" in modo che più unità di traduzione possano definirla:

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

Oppure puoi semplicemente spostare la definizione originale della funzione nel file sorgente "complex.cpp".

Il compilatore non si lamenta di "real ()" perché è implicitamente in linea (qualsiasi funzione membro il cui corpo è dato nella dichiarazione di class viene interpretata come se fosse stata dichiarata "in linea"). Le protezioni del preprocessore impediscono che l'intestazione venga inclusa più di una volta da una singola unità di traduzione (file sorgente "* .cpp"). Tuttavia, entrambe le unità di traduzione vedono lo stesso file di intestazione. Fondamentalmente, il compilatore compila "main.cpp" in "main.o" (comprese le definizioni fornite nelle intestazioni incluse da "main.cpp"), e il compilatore compila separatamente "complex.cpp" in "complex.o" (comprese le definizioni fornite nelle intestazioni incluse da "complex" .cpp "). Quindi il linker unisce" main.o "e" complex.o "in un singolo file binario: è a questo punto che il linker trova due definizioni per una funzione con lo stesso nome. indica che il linker tenta di risolvere i riferimenti esterni (ad es. "main.o" si riferisce a "Complesso: Complesso" ma non ha una definizione per quella funzione ... il linker individua la definizione da "complex.o" e risolve tale riferimento).

E c’è un’altra soluzione come usare la parola chiave inline ?

Si C’è. Oltre a definire il metodo all’interno del file di implementazione complex.cpp come menzionato da altri, è anche ansible inserire la definizione in uno spazio dei nomi senza nome.

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

In pratica, questo creerà uno spazio dei nomi univoco per ciascuna unità di compilazione. In questo modo, previeni il nome scontro. Tuttavia, i nomi vengono ancora esportati dall'unità di compilazione ma inutili (poiché i nomi sono sconosciuti).

Mettere la definizione all'interno del file di implementazione è spesso una soluzione migliore. Tuttavia, per i modelli di classi non è ansible farlo poiché i compilatori C ++ non supportano i modelli di istanziazione in un'unità di compilazione diversa da quella in cui sono stati definiti. In questi casi, è necessario utilizzare uno spazio dei nomi inline o non denominato.

Sposta l’implementazione in complex.cpp

In questo momento, dopo aver incluso questa implementazione del file, viene compilato su ogni file. Più tardi durante il collegamento c’è un evidente conflitto a causa di implementazioni duplicate.

:: real () non viene segnalato perché è implicito in linea (implementazione all’interno della definizione della class)

Stavo avendo questo problema, anche dopo che la mia fonte e il file di intestazione erano corretti.

E ‘risultato che Eclipse stava usando artefatti stantii da una build precedente (fallita).

Per risolvere il problema, utilizzare Project > Clean quindi ribuild.