Alarmnummer schreef op 28 november 2003 @ 09:42:
[...]
In .NET gebeurt dit wel, gelukkig vind dit runtime plaats (lees anders de documentatie van Gyro maar eens). Dit heeft als voordeel dat dat je niet van te voren alles hoeft uit te compileren (voor alle types de code te maken)
compileren voor andere typen, zelfs at runtime, lijkt me een beetje nutteloos. Immers, je template parameters voldoen aan een bepaalde specificatie, het moet een bepaalde interface implementeren of een class extenden. Je kunt dus prima met de base class werken.
Het verschil in code zit 'm vervolgens in het gebruik van die template functie of class. Want als je een generic functie hebt zoals bijvoorbeeld (en ik ken de notatiewijze verder niet, maar dit is wel begrijpbaar denk ik)
Java:
1
| generic<T extends Object> T eenFunctie (T t); |
En je stopt er een MyClass in, dan verwacht je er ook een MyClass weer uit. Intern werkt de functie echter gewoon op Object. In feite zijn de volgende 2 regels code dan ook identiek:
Java:
1
2
| MyClass c = eenFunctie<MyClass> (c);
MyClass c = (MyClass)eenFunctie ((Object)c); |
(waarbij de cast naar Object op regel 2 slechts ter illustratie dient)
Intern aan eenFunctie hoeft er niets te veranderen aan de code. Wat erbij komt is slechts een cast op het eind naar MyClass (en de impliciete cast naar Object bij het argument).
En daarom is het mijs inziens ook fundamenteel anders dan C++ templates, waar er een lexicale substitutie plaatsvindt bij de template parameters.
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
| template <class T>
void roepEenFunctieAan (T & t)
{
t.eenFunctie ();
}
struct A
{
void eenFunctie () { std::cout << "hoi" << std::endl; }
};
struct B
{
struct functor
{
int operator () (int i = 0)
{
return i + 10;
}
};
functor eenFunctie;
};
int main ()
{
A a;
B b;
roepEenFunctieAan (a);
roepEenFunctieAan (b);
} |
A::eenFunctie en B::eenFunctie zijn compleet anders. Bij A is het gewoon een functie zonder parameters en returntype. Bij B is het een object, dat door middel van operator overloading aan te roepen is als een functie. En het heeft bovendien een compleet andere signature dan A::eenFunctie. A en B hebben totaal niets met elkaar gemeen, ze voldoen niet aan een bepaalde gespecificeerde interface oid. Dit werkt ook alleen maar als roepEenFunctieAan<A> en roepEenFunctieAan<B> totaal verschillende stukjes code produceren. En dit werkt dan ook alleen maar als de implementatie at compile-time bekend is (met uitzondering van het export keyword, waarmee de compilatie verplaatst kan worden naar link-time)
Ik moet er overigens wel bijzeggen dat ik eigenlijk alleen java generics ken, en alleen maar een beetje (door discussies van oa mbravenboer

). Uit de presentatie van visual c++ whidbey heb ik begrepen dat generics in .net ook ongeveer zo werkt als in java, en heel anders is dan c++ templates (anders is het ook vrij onlogisch om generics naar c++ te brengen, wat dus gebeurt in whidbey

). Ik zal vanavond als ik tijd heb de docs van Gyro eens doornemen, lijkt me wel interessant