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 reference
Typ beispielsweise völlig anders aussieht als bei anderen T
s. Zugegeben, es ändert nicht die Art von reference
Typ zu etwas anderem, aber es könnte trotzdem passieren.
Was passiert nun, wenn Sie mit dieser test
Vorlage 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>::ptr
ein 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>::ptr
ist 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 typename
vorher 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.)