Schau dir mal das folgende abgespeckte Beispiel an, in dem etwas
Ähnliches passiert wie bei unordered_set:
1 | struct Foo;
|
2 |
|
3 | template<typename T>
|
4 | struct Bar {
|
5 | typedef int myint;
|
6 | T t;
|
7 | };
|
8 |
|
9 | Bar<Foo>::myint mi; // Fehler
|
10 | Bar<Foo>::myint *pmi; // Fehler
|
Eigentlich sollte es kein Problem sein, die Variable pmi vom Typ
Bar<Foo>::myint * zu deklarieren, denn:
1. Der Typ myint ist unabhängig vom Template-Argument T immer ein
Synonym zu int.
2. Selbst wenn myint ein unsvollständiger Typ wäre, müsste es immer noch
möglich sein, wenigstens einen Zeiger (pmi) darauf zu deklarieren.
Das Problem liegt aber wo ganz anders:
In dem Moment, wo ein Element einer Klasse verwendet wird, und sei es
nur wie hier eine Typdeklaration, muss der Compiler in die Klasse
hineinsehen können. Das kann er aber nur, wenn er die Klassendeklaration
vor sich liegen hat. Bei Foo<Bar> existiert jedoch zunächst nur das
Template. Um das Element myint ansehen zu können, instanziiert der
Compiler dieses Template, und erhält damit folgende Klassendeklaration:
1 | struct Bar<Foo> {
|
2 | typedef int myint;
|
3 | Foo t;
|
4 | };
|
Beim Durchlesen dieser Deklarieren stellt der Compiler fest, dass darin
die Member-Variable t von einem unvollständigen Typ, nämlich Foo, ist.
Damit ist die Klassendeklaration fehlerhaft, und der Compiler meckert
schon, bevor er auch nur einen Versuch unternimmt, das Element myint zu
verwenden.
Wäre die Member-Variable t nicht vom Typ T bzw. Foo, sondern nur ein
Zeiger darauf, wäre die Klassendeklaration korrekt, da Deklarationen von
Zeigern auf unvollständige Typen immer erlaubt sind. Damit würde auch
die Template-Instanziierung funktionieren:
1 | struct Foo;
|
2 |
|
3 | template<typename T>
|
4 | struct Bar {
|
5 | typedef int myint;
|
6 | T *pt;
|
7 | };
|
8 |
|
9 | Bar<Foo>::myint mi; // kein Fehler
|
10 | Bar<Foo>::myint *pmi; // kein Fehler
|
Es scheint zunächst unbegreiflich, dass der Typ einer Membervariable
einer Klasse, die überhaupt nicht instanziiert wird, zu einer
Fehlermeldung führt, aber wenn man sich die Logik von Templates und
Klassendeklarationen in C++ vor Augen hält, ist es eben einfach so.
Dass bei den Container-Typen set<T>, vector<T> usw. das Problem nicht
auftritt, liegt daran, dass dort keine Member-Variablen vom Typ T,
sondern nur welche vom Typ T* verwendet werden und auch nirgends ein
sizeof auf den T angewendet wird.