Linker Fehler mit statischem Template Member

Kamikaze

Warrior of Sunlight
Teammitglied
Ich habe folgenden Schnipsel Code:
Code:
template <class type, bool const cd = true>
class RingBuffer final {
	private:
	/**
	 * Allocator to reserve buffer memory without calling
	 * constructors.
	 */
	static std::allocator<type> allocator;
[…]

Später im Template wird an mehreren Stellen auf allocator zugegriffen. Ich habe es sowohl mit this->allocator als auch RingBuffer<type, cd>::allocator (meine bevorzugte Variante, weil man dem Zugriff ansieht, dass allocator statisch ist) probiert.

Mit -O1 oder -O2 geht das auch ohne Probleme:
Code:
# make CXXFLAGS="-O2 -pipe -march=native -g"
c++ -O2 -pipe -march=native -g -std=c++11  -o bin/main.o -c src/main.cpp
[…]
c++ -O2 -pipe -march=native -g -lpthread  -o bin/main bin/main.o bin/io/Parse.o bin/io/Exception.o bin/verse/Vertex.o bin/verse/Triangle.o bin/verse/Orientation.o bin/verse/Transform.o bin/io/Session.o bin/model/Tool.o bin/model/Stock.o bin/model/Machine.o bin/verse/Mesh.o bin/octree/Tree.o bin/octree/Node.o bin/octree/SolidVolumeCounter.o bin/octree/NodeCounter.o bin/octree/NodeVisitor.o bin/math/WideInteger.o bin/octree/Serialise.o

Anders sieht die Sache leider ohne Optimierungen aus:
Code:
# make CXXFLAGS="-g -pipe"
c++ -g -pipe -std=c++11  -o bin/main.o -c src/main.cpp
[…]
c++ -g -pipe -lpthread  -o bin/main bin/main.o bin/io/Parse.o bin/io/Exception.o bin/verse/Vertex.o bin/verse/Triangle.o bin/verse/Orientation.o bin/verse/Transform.o bin/io/Session.o bin/model/Tool.o bin/model/Stock.o bin/model/Machine.o bin/verse/Mesh.o bin/octree/Tree.o bin/octree/Node.o bin/octree/SolidVolumeCounter.o bin/octree/NodeCounter.o bin/octree/NodeVisitor.o bin/math/WideInteger.o bin/octree/Serialise.o
bin/main.o: In function `io::RingBuffer<Foo, true>::RingBuffer(unsigned long, unsigned long)':
/usr/home/kamikaze/stark/Yggdrasil/src/io/RingBuffer.hpp:133: undefined reference to `io::RingBuffer<Foo, true>::allocator'
bin/main.o: In function `io::RingBuffer<Foo, true>::cNext()':
/usr/home/kamikaze/stark/Yggdrasil/src/io/RingBuffer.hpp:244: undefined reference to `io::RingBuffer<Foo, true>::allocator'
bin/main.o: In function `io::RingBuffer<Foo, true>::~RingBuffer()':
/usr/home/kamikaze/stark/Yggdrasil/src/io/RingBuffer.hpp:145: undefined reference to `io::RingBuffer<Foo, true>::allocator'
c++: error: linker command failed with exit code 1 (use -v to see invocation)
*** Error code 1

Das Problem bin ich natürlich los, wenn ich allocator nicht statisch mache. Aber ich brauche halt nur einen für alle Instanzen einer RingBuffer<> Variante.

Mache ich hier etwas falsch, oder ist das ein Compiler Fehler? Oder ein Linker Fehler?

Ich könnte mir vorstellen, dass Static in Templates verboten ist, weil das Static Attribut dann in mehreren Object files initialisiert wird. Andererseits könnte der Linker das ja wieder zusammenschustern. Macht er ja mit -O1/2 anscheinend auch.
 
Hast du das Programm mal mit dem gcc getestet. Wenn gcc damit keine Probleme hat, ist es umso wahrscheinlicher, das etwas nicht stimmt.
 
Das ich die Definition vergessen habe stimmt - aber warum funktioniert es mit -O2 trotzdem? Da der allocator auch verwendet wird, kann der nicht weg optimiert werden.
 
Gerade im Zusammenhang mit Templates ist es immer wieder erstaunlich, was so ein C++ Compiler alles optimiert... speziell im Hinblick auf Inlining. Und was gültiger Code ist (Stichwort: SFINAE)...

Da Du scheinbar den Standard-Allocator verwendest, ist das nichts als eine andere Art "new" zu schreiben. Weil der Compiler in dem Modul, das den Allocator verwendet, auch die Definition aus allocator.h kennt, ist es nicht verwunderlich, dass er den Aufruf aus Effizienzgründen inline durchführt. Da mit -O0 alle Optimierungen abgeschaltet sind, d.h. auch automatisches Inlining, siehst Du den Fehler vom Linker, wenn Du mit -O0 kompilierst.

Vielleicht kannst Du Dir mal den generierten Assembler Code mit -O1 anschauen. Ich würde fast darauf wetten, dass die Calls zum Allocator inline ausgeführt werden. Verwendest Du irgendwo die Adresse von Deiner allocator Variable, z.B. weil Du sie als Parameter oder Referenz weiter reichst? Falls nicht, dann ist's auch durchaus gültig, dass im Objectcode keine Spuren mehr von Deiner allocator Variable auftauchen und der Linker den Fehler gar nicht mehr sieht bzw. sich -O0 vs. -O1 anders verhalten.
 
Ich denke immer war kaputt ist sollte nicht bauen, egal welche Optimierung. Aber das geht ja bei Fehlern, die erst der Linker sehen kann gar nicht.
 
Zurück
Oben