Das Geheimnis liegt in der Tatsache, dass eine Vorlage für einige Typen spezialisiert werden kann. Dies bedeutet, dass die Schnittstelle für verschiedene Typen völlig unterschiedlich definiert werden kann. Zum Beispiel können Sie schreiben:
template<typename T>
struct test {
typedef T* ptr;
};
template<> // complete specialization
struct test<int> { // for the case T is int
T* ptr;
};
Man könnte fragen, warum dies nützlich ist und in der Tat: Das sieht wirklich nutzlos aus. Beachten Sie jedoch, dass std::vector<bool>der referenceTyp beispielsweise völlig anders aussieht als bei anderen Ts. Zugegeben, es ändert nicht die Art von referenceTyp zu etwas anderem, aber es könnte trotzdem passieren.
Was passiert nun, wenn Sie mit dieser testVorlage eigene Vorlagen schreiben? Etwas wie das
template<typename T>
void print(T& x) {
test<T>::ptr p = &x;
std::cout << *p << std::endl;
}
Es scheint für Sie in Ordnung zu sein, weil Sie erwarten, dass dies test<T>::ptrein Typ ist. Aber der Compiler weiß es nicht und in der Tat wird ihm vom Standard sogar geraten, das Gegenteil zu erwarten, es test<T>::ptrist kein Typ. Um dem Compiler mitzuteilen, was Sie erwarten, müssen Sie zuvor ein hinzufügen typename. Die richtige Vorlage sieht so aus
template<typename T>
void print(T& x) {
typename test<T>::ptr p = &x;
std::cout << *p << std::endl;
}
Fazit: Sie müssen typenamevorher hinzufügen, wann immer Sie einen verschachtelten Typ einer Vorlage in Ihren Vorlagen verwenden. (Natürlich nur, wenn ein Vorlagenparameter Ihrer Vorlage für diese innere Vorlage verwendet wird.)