typnamn
" typnamn
" är ett nyckelord i programmeringsspråket C++ som används när man skriver mallar . Det används för att ange att ett beroende namn i en malldefinition eller deklaration är en typ. I de ursprungliga C++-kompilatorerna innan den första ISO-standarden var klar nyckelordet
typnamn inte en del av C++-språket och Bjarne Stroustrup använde istället klassnyckelordet
för mallargument. Medan typnamn
nu är det föredragna nyckelordet kan äldre källkod fortfarande använda klassnyckelordet istället
(se till exempel skillnaden i källkodsexempel mellan The Design and Evolution of C++ av Bjarne Stroustrup publicerad 1994 och källkodsexemplen i The C++ Programming Språk: Fjärde upplagan av Bjarne Stroustrup publicerad 2013).
En synonym för " klass
" i mallparametrar
I C++s generiska programmeringsfunktion känd som " mallar ", kan typnamn
användas för att introducera en mallparameter :
// Definiera en generisk funktion som returnerar det största av dess två argument mall < typnamn T > const T & max ( const T & x , const T & y ) { if ( y < x ) return x ; returnera y ; }
Ett alternativt och semantiskt likvärdigt nyckelord i detta scenario är " klass
":
// Definiera en generisk funktion som returnerar det största av dess två argument mall < class T > const T & max ( const T & x , const T & y ) { if ( y < x ) return x ; returnera y ; }
En metod för att indikera att ett beroende namn är en typ
Tänk på den här ogiltiga koden:
mall < typnamn T > void foo ( const T & t ) { // deklarerar en pekare till ett objekt av typen T::bar T :: bar * p ; // error (se text) } struct StructWithBarAsType { typedef int bar ; }; int main () { StructWithBarAsType x ; foo ( x ); }
Den här koden ser ut som den borde kompilera, men den är felaktig eftersom kompilatorn inte vet om T::bar
är en typ eller ett värde. Anledningen till att den inte vet är att T::bar
är ett "mallparameterberoende namn", eller förkortat "beroende namn", som sedan kan representera allt som heter "bar" inuti en typ som skickas till foo(), vilket kan inkludera typedefs , enums , variabler etc.
För att lösa denna tvetydighet deklarerar C++ Language Standard :
Ett namn som används i en malldeklaration eller definition och som är beroende av en mallparameter antas inte namnge en typ om inte den tillämpliga namnsökningen hittar ett typnamn eller namnet kvalificeras av nyckelordet
typnamn
.
Kort sagt, om kompilatorn inte kan avgöra om ett beroende namn är ett värde eller en typ, kommer den att anta att det är ett värde.
I vårt exempel, där T::bar
är det beroende namnet, betyder det att snarare än att deklarera en pekare till T::bar med
namnet p
, raden
T::bar * p;
kommer istället att multiplicera "värdet" T::bar
med p
(som ingenstans finns) och slänga resultatet. Det faktum att den beroende stapeln i
StructWithBarAsType
faktiskt är en typ hjälper inte eftersom foo()
kunde kompileras långt innan StructWithBarAsType
ses. Dessutom, om det också finns en klass som:
struct StructWithBarAsValue { int bar ; };
då skulle kompilatorn vara skyldig att tolka T::baren
i foo()
som en åtkomst till datamedlemmen StructWithBarAsValue::bar
när den instansierades. Men eftersom bar
inte är en statisk datamedlem kommer den att flagga ett fel.
Lösningen på detta problem är att uttryckligen tala om för kompilatorn att T::bar
faktiskt är en typ. För detta används nyckelordet typnamn :
mall < typnamn T > void foo ( const T & t ) { // deklarerar en pekare till ett objekt av typen T::bar typnamn T :: bar * p ; }
Nu vet kompilatorn med säkerhet att T::bar
är en typ, och kommer korrekt att göra p
till en pekare till ett objekt av den typen.
Se även
- Argumentberoende namnsökning – en annan C++-namnsökningsregel