Include-Problem: Multiple definitions
-
Folgende Dateien sind vorhanden:
//main.cpp #include <iostream> #include "header.h" #include "alpha.h" int bar() { std::cout << foo() << std::endl; } int main() { bar(); alpha(); return 0; }
// header.h #ifndef HEADER_H_ #define HEADER_H_ int foo() { return 8; } #endif
// alpha.h #ifndef ALPHA_H_ #define ALPHA_H_ void alpha(); #endif
// alpha.cpp #include "alpha.h" #include "header.h" #include <iostream> void alpha() { std::cout << foo() << std::endl; }
Dier erhoffte Ausgabe ist natürlich zweimal die "8". Leider fliegt mir das ganze um die Ohren:
g++ -Wall -Wno-long-long -Df2cFortran -O3 -DNDEBUG -o bin/foo build/alpha.o build/main.o build/main.o: In function `foo()': main.cpp:(.text+0x0): multiple definition of `foo()' build/alpha.o:alpha.cpp:(.text+0x0): first defined here
Wie kann ich das Auflösen? Ich würde gerne foo() in einem header lassen (ohne cpp Datei). In meiner "echten" Anwendung stehen auch template Funktionen in header.h, und die sind ja gerne mal etwas umständlich, wenn man sie in mehrere Dateien aufteilt.
Mir ist auch nicht ganz klar, warum es überhaupt eine Mehrfachdefinition geben soll. Die sollten doch die #ifndef ... #endif Anweisungen verhindern.
-
int foo() -> static int foo()
-
Mups schrieb:
Mir ist auch nicht ganz klar, warum es überhaupt eine Mehrfachdefinition geben soll. Die sollten doch die #ifndef ... #endif Anweisungen verhindern.
Die Definition der Funktion erscheint durch das #include in mehreren Objekt-Dateien, was den Linker stört. Der fügt den ganzen Stuss ja am Schluss zusammen und mehrfache Symbole gleichen Namens funktionieren da nicht (um genau zu sein geht das u.U. schon, ist hier aber egal).
-
Mups schrieb:
Wie kann ich das Auflösen? Ich würde gerne foo() in einem header lassen (ohne cpp Datei).
Warum?
Mups schrieb:
In meiner "echten" Anwendung stehen auch template Funktionen in header.h, und die sind ja gerne mal etwas umständlich, wenn man sie in mehrere Dateien aufteilt.
Kontext?
Mups schrieb:
Mir ist auch nicht ganz klar, warum es überhaupt eine Mehrfachdefinition geben soll. Die sollten doch die #ifndef ... #endif Anweisungen verhindern.
#ifndef/#endif verhindert Mehrfach-Deklarationen/Definitionen innerhalb einer Übersetzungseinheit. Du hast zwei Übersetzungseinheiten:
1. <iostream> + "header.h" + "alpha.h" + "main.cpp"
2. "alpha.h" + "header.h" + <iostream> + "alpha.cpp"#include ist einfach eine Textersetzung. Dem Compiler ist es egal, woher der Kram kommt, er "sieht" nur das Endergebnis der Textersetzung. Dementsprechend hast Du jeweils eine Definition von foo je Übersetzungseinheit.
Lies Dir mal das Kapitel in Deinem C++ Buch über die "one definition rule" durch.
-
inline
4tw ;o)
-
krümelkacker schrieb:
Mups schrieb:
In meiner "echten" Anwendung stehen auch template Funktionen in header.h, und die sind ja gerne mal etwas umständlich, wenn man sie in mehrere Dateien aufteilt.
Kontext?
siehe zum Beispiel hier (Abschnitt 7):
http://www.c-plusplus.net/forum/viewtopic-var-t-is-128361-and-postdays-is-0-and-postorder-is-asc-and-start-is-0.html(wobei ich oben mit "mehrere Dateien" eine .h und eine .cpp Datei meinte)
krümelkacker schrieb:
#ifndef/#endif verhindert Mehrfach-Deklarationen/Definitionen innerhalb einer Übersetzungseinheit. Du hast zwei Übersetzungseinheiten:
1. <iostream> + "header.h" + "alpha.h" + "main.cpp"
2. "alpha.h" + "header.h" + <iostream> + "alpha.cpp"Ok, das war mir nicht klar. Jetzt verstehe ich das Problem.
inline 4tw ;o)
Das funktioniert.
Danke euch allen
-
Mups schrieb:
krümelkacker schrieb:
Mups schrieb:
In meiner "echten" Anwendung stehen auch template Funktionen in header.h, und die sind ja gerne mal etwas umständlich, wenn man sie in mehrere Dateien aufteilt.
Kontext?
siehe zum Beispiel hier (Abschnitt 7):
http://www.c-plusplus.net/forum/viewtopic-var-t-is-128361-and-postdays-is-0-and-postorder-is-asc-and-start-is-0.htmlWas hat das mit Deiner Funktion zu tun? Eine Funktion, die nicht inline ist und kein Template ist, definiert und kompiliert man höchstens einmal. Der Sinn von Headerdateien ist eigentlich nur, so eine Funktion anderen ÜEs gegenüber bekannt zu machen -- also, dass es sie gibt, und weniger, wie sie implementiert ist.
Mups schrieb:
inline 4tw ;o)
Das funktioniert.
Ich würde Dir raten, bevor Du einfach "inline" vor eine Funktionsdefinition schreibst, Dich damit auseinander zu setzen, was "inline" eigentlich macht. Inline ist nicht dazu da, Funktionsdefinitionen in Headerdateien zu verlagern. Das kann gar kein Ziel sein. Wozu sollte man das wollen? Dass man Inline-Funktionen in Headerdateien definieren kann ist eher ein Nebeneffekt. Man muss es zulassen, damit Inlining überhaupt funktionieren kann.
-
krümelkacker schrieb:
Was hat das mit Deiner Funktion zu tun? Eine Funktion, die nicht inline ist und kein Template ist, definiert und kompiliert man höchstens einmal. Der Sinn von Headerdateien ist eigentlich nur, so eine Funktion anderen ÜEs gegenüber bekannt zu machen -- also, dass es sie gibt, und weniger, wie sie implementiert ist.
Wie oben schon geschrieben: In meiner echten Anwendung stehentemplate-Funktionen in header.h drin. Die habe ich hier nicht gepostet, weil ich ein hübsch kleines Beispiel haben wollte (abgesehen davon, dass ich meinen echten Code gar nicht posten darf...)
Aber wenn du es lieber magst, schreiben wir sowas in header.h rein:
template<class T> inline void foo(T t) { std::cout << t << "*2 == " << t*2 << std::endl; }
krümelkacker schrieb:
Ich würde Dir raten, bevor Du einfach "inline" vor eine Funktionsdefinition schreibst, Dich damit auseinander zu setzen, was "inline" eigentlich macht.
Keine Sorge: Ich weiß, was ich tue
Mir gefällt das eigentlich sogar recht hübsch, weil meine template-Funktionen sogar recht kurz sind.
-
Mups schrieb:
krümelkacker schrieb:
Ich würde Dir raten, bevor Du einfach "inline" vor eine Funktionsdefinition schreibst, Dich damit auseinander zu setzen, was "inline" eigentlich macht.
Keine Sorge: Ich weiß, was ich tue
Mir gefällt das eigentlich sogar recht hübsch, weil meine template-Funktionen sogar recht kurz sind.
Wenn du die inline-Geschichte verstanden hättest dann hätte die Frage nicht kommen dürfen weil du dann mit der ODR und der ganzen Linker-Geschichte vertraut sein müsstest.
Und mit templates hat das garnichts zu tun, weil da die Situation eh ne grundverschiedene ist und templatefunktionen grundsätzlich im Header definiert sein müssen.
-
pumuckl schrieb:
Und mit templates hat das garnichts zu tun, weil da die Situation eh ne grundverschiedene ist und templatefunktionen grundsätzlich im Header definiert sein müssen.
Aber eben darum gehts mir doch. Vielleicht war mein Beispielproblem oben schlecht gewählt. Stelle dir mein Beispiel da ganz oben vor, bloß das foo() eine template-Funktion ist. Und dann sage mir eine Lösung, die ohne inline auskommt. Dann wäre mir tatsächlich noch weit mehr geholfen.
Der einzigen Dreher in meinem Kopf war, dass ich dachte, #ifndef ... #endif würde für alle und nicht bloß für eine Übersetzungseinheit gelten. Nicht ganz klug von mir, aber dafür ist dies feine Forum ja da.
-
Mups schrieb:
Vielleicht war mein Beispielproblem oben schlecht gewählt.
Ja, war es, eben aus besagtem Grund, weil das Linekn bei Funktionstemplates und normalen Funktionen völlig unterschiedlich ist.
Stelle dir mein Beispiel da ganz oben vor, bloß das foo() eine template-Funktion ist. Und dann sage mir eine Lösung, die ohne inline auskommt. Dann wäre mir tatsächlich noch weit mehr geholfen.
Indem du foo zum Template machst und das inline einfach weglässt. Der von dir beschriebene Linkerfehler taucht bei Templates nicht auf, wiel Templates die ODR nicht verletzen.
-
Mups schrieb:
Aber wenn du es lieber magst, schreiben wir sowas in header.h rein:
template<class T> inline void foo(T t) { std::cout << t << "*2 == " << t*2 << std::endl; }
[...]
Keine Sorge: Ich weiß, was ich tueMir gefällt das eigentlich sogar recht hübsch, weil meine template-Funktionen sogar recht kurz sind.
Warum hast Du hier inline verwendet?
Um ehrlich zu sein, ich habe nicht den Eindruck, Du wüsstest, was Du tust. Dir ist scheinbar "die ODR" (one definition rule) nicht bekannt. Die ODR lässt sich auch nicht in einem Satz zusammenfassen. Sie ist detaillierter als Du denkst. Das brauchst Du nicht persönlich nehmen. Ich hake deswegen nach, um Dir zu helfen / um Missverständnisse auszuschließen.
-
Keine Sorge, so schnell nehme ich nix persönlich
Dann habe ich tatsächlich in meinem Beispiel zu viel weggelassen, um es zu minimieren. Ich geh jetzt nachsitzen und lese nochmal ein bisschen. Vielen Dank euch allen
-
Dann kann ich dir sagen was falsch ist
Du hast dein template spezialisiert und diese Speizialisierung im Header implementiert. Spezialisierungen gehören aber in ein .cpp. Denn eine Spezialisierung ist im Gegnsatz zum allgemeinen template bereits konkret gegeben und wird nicht erst bei Verwendung mit einem Typen erstellt.
-
l'abra d'or schrieb:
Dann kann ich dir sagen was falsch ist
Du hast dein template spezialisiert und diese Speizialisierung im Header implementiert. Spezialisierungen gehören aber in ein .cpp. Denn eine Spezialisierung ist im Gegnsatz zum allgemeinen template bereits konkret gegeben und wird nicht erst bei Verwendung mit einem Typen erstellt.Das kann natürlich auch sein. Da aber a) Funktionstemplates nicht partiell spezialisiert werden können, b) vollstädnige Spezialisierungen von Funktionstemplates bereits normale Funktionen sind (und daher eh das beschriebene ODR-Problem haben) und c) vom Compiler normale Funktionen den Templatefunktionen vorgezogen werden, sollten wenn möglich die Funktionstemplates mit entsprechenden normalen Funktionen überladen werden, statt sie zu spezialisieren. Möglich ist das genau dann wenn alle Templateparameter aus der Argumentliste hergeleitet werden können, wie im vorliegenden Fall.
Also statt
template <class T> void foo(T t) { /* ... */ } template <> void foo<int>(int i) { /* ODR verletzt, falls in Header und nicht inline */ }
lieber gleich
template <class T> void foo(T t) { /* ... */ } void foo(int i) { /* ODR verletzt, falls in Header und nicht inline */ }
Wie vom Vorposter gesagt sollten beidesmal die Definitionen in die .cpp oder aber inline deklariert werden - oder statt freien Funktionen Klassenmethoden benutzen, die eh implizit inline sind wenn man sie direkt in der Klassendefinition mit definiert.