TechBlog cikkei

Osztály template csak meghatározott típusokkal

Mint már megszokhattátok tőlem, általában programozási ötletekért keresem fel a TechBlogot.

Ez most sincs másképp...

A nyelv C++, a kérdés pedig, hogy tudok-e olyan osztály template-et csinálni, ami csak bizonyos típusokból engedi megcsinálni a template-et? Konkrétan, szeretnék egy olyan osztály template-et csinálni, ami csak double vagy egy bizonyos saját típus (nevezzük mondjuk __CLPK_doublecomplexnek) felett működik, mással nem.

Köszi a segítséget!

Utoljára módosította SAdam 2009.X.02 18:40-n
Bejegyzés módosítása | PermaLink
Szavazás letiltva.

Hozzászólások

8

UPi 2009.X.02 19:11

Arról írnál, hogy miért jó egy template osztályt korlátozni? Számomra dupla-plusz szükségtelennek tűnik, ha nem akarod, hogy int-re specializálódjon, akkor ne specializáld int-re...


SAdam 2009.X.02 20:16

Egyszerű. Van egy Matrix nevű osztályom, amire definiáltam az összes lehetséges operator overloading-ot, amire a számításaim során szükségem lehet, ezen kívül van egy rutin, ami kiadja a sajátértékeit, meg a bal- és jobboldali sajátvektorait. Ez a Matrix osztály jelenleg komplex számok formájában (konkrétan, az általam említett __CLPK_doublecomplex típusként) tárolja el magát a mátrixot.

Most úgy adódott, hogy szükségem lenne egy valós mátrixot reprezentáló osztályra is. Ezért két lehetőségem van: vagy definiálok egy új osztályt, ahol a mátrix értékeit doubleként tárolom, és szó szerint lemásolom az összes operator overloading-ot, amit csak csináltam (plusz átírom a sajátértékeket számító függvényt), vagy csinálok a már meglévő osztályból egy template osztályt, amit utána használhatok Matrix<double> vagy Matrix<__CLPK_doublecomplex> formában (ez utóbbi sokkal olvashatóbbá teszi a kódot, továbbá megkímél attól, hogy duplikálnom kelljen feleslegesen egy csomó kódot).

Azért szeretném, ha mindenképp explicit definiálni tudnám azt a halmazt, amiből a mátrix készülhet, mert nem akarok olyat megengedni, hogy definiálni lehessen mondjuk Matrix<bool>, vagy valami hasonló hülyeséget, aminek semmi értelme. Ráadásul kipróbáltam, hogy ha "csak úgy" csinálok belőle egy template-et, akkor nem fog lefordulni a kód, mert nem fogja tudni, hogy egy általános T osztályra hogyan értelmezze azt, hogy mondjuk "return T + 2", míg ha T explicit módon korlátozva van a double vagy __CLPL_doublecomplex típusok valamelyikére, akkor a fenti kifejezést a fordítónak (legalábbis tippem szerint) mindenképp kell tudnia értelmezni.


SAdam 2009.X.02 20:23

Bocs, most esett le, hogy tul. képp. mit kérdeztél. Szóval a válasz, hogy szeretném, ha a kód lefordulna. Jelenleg, ha csak úgy deklarálom az osztályom, hogy

 template <class T>
 class Matrix { ... };

akkor a fordító hibát dob, az előző levelemben ismeretett ok miatt. Szóval, az is jó, ha valaki tud adni egy tippet, hogy mit kell ahhoz csinálni, hogy a fordító "elhigyje" nekem, hogy a T osztály csak olyan dolgokból fog kikerülni, amiket valóban lehet összeadni, szorozni, satöbbi. Ha erre van megoldás, akkor persze már nem fogom akarni megszorítani az osztályt, hogy csak double vagy __CLPK_doublecomplex alapon lehessen definiálni...


UPi 2009.X.02 20:34

Még mindig nem értem, hogy mi a probléma, ugyanis amíg nincs konkrét T, addig a fordító nem fordít neked a template-ből semmit. Vagyis, ha ezt írod:

template <class T> class valami { T value; public: valami(T _value) : value(_value) {} T multiplyByTen() { return T * 10; } }; valami<int> i(10); int j = i.multiplyByTen(); // Ez működik. valami<string> s("kutya"); string r = s.multiplyByTen(); // Ez nem fordul le, mert nincs string.operator*(int) függvény.

Magyarán, amíg nem csinálsz valamit, amitől olyan kód fordul, ami helytelen, addig nincs gond a template-tel. Vagyis még mindig nincs okod külön erőfeszítést tenned azért, hogy ne tudd példányosítani a template-et rossz paraméterrel: ezt a fordító megteszi helyetted.


UPi 2009.X.02 20:35

(Válaszképp erre)

Bocs, az előző kommentben T multiplyByTen() { return _value * 10; } akart lenni..


SAdam 2009.X.02 20:38

(Válaszképp erre)

<blockquote> Még mindig nem értem, hogy mi a probléma, ugyanis amíg nincs konkrét T, addig a fordító nem fordít neked a template-ből semmit. Vagyis, ha ezt írod:

template <class T> class valami { T value; public: valami(T _value) : value(_value) {} T multiplyByTen() { return T * 10; } }; valami<int> i(10); int j = i.multiplyByTen(); // Ez működik. valami<string> s("kutya"); string r = s.multiplyByTen(); // Ez nem fordul le, mert nincs string.operator*(int) függvény.

Magyarán, amíg nem csinálsz valamit, amitől olyan kód fordul, ami helytelen, addig nincs gond a template-tel. Vagyis még mindig nincs okod külön erőfeszítést tenned azért, hogy ne tudd példányosítani a template-et rossz paraméterrel: ezt a fordító megteszi helyetted. </blockquote>

Akkor valamit elb*sztam... Nyüff. Nekifutok megint, és ha még mindig nem megy, akkor jelentkezem megint...


SAdam 2009.X.05 04:14

Na, két napi nekifutás után még mindig nem megy, de legalább kezd fény derengeni...

Szóval addig eljutottam, hogy a template-ekből fordítási időben készülnek el a konkrét osztályok, ezért ha fordítási időben rendelkezésre áll minden, akkor nincs gond, le fog fordulni a program, vagyis nem kell explicit módon megadnom, hogy miből engedem meg létrehozni a konkrét ojjektumot, mert a fordító úgyis jelez, ha hiba van.

Na most a problémám már csak az, hogy hogyan tudok "feltételes fordítást" csinálni annak alapján, hogy milyen típus alapján készült a template? Magyarul, vagy egy rutinom (konkrétan, az a rutin, ami a mátrix sajátértékeit számolja), ami más algoritmust kellene, hogy futtasson akkor, ha a mátrix komplex, mint akkor, ha a mátrix valós. Első blikkre a dynamic_cast paranccsal próbáltam megoldani a dolgot (úgy, hogy az eljárás elején definiáltam egy T típusú változót, és tesztelem, hogy mivé tudom castolni, és ez alapján egy elágazás szerint csinálom a csinálnivalót), de ez egyrészt csúnya, másrészt valahogy nem akar működni, ugyanis a fordító már fordítási időben közli, hogy nem fog menni a castolás, ezért le sem fordítja a dolgot. Van erre valami szép módszer, hogy külön definiálni tudjam a Matrix<double> és a Matrix<__CLPK_complex> osztályok ugyanolyan nevű (legyen mondjuk getEigenValues()) metódusát?

A másik hasonlóan problémás dolog: lehet olyat csinálni, hogy definiáljam az általános Matrix<T> osztály egy eljárását valamilyen T típusú paraméterrel, és külön megadhassam azt is, hogy T-től függetlenül egy double paraméterre hogyan reagáljon ugyanez az eljárás? Konkrétan valami ilyesmire gondoltam:

 const Matrix<T> & operator = ( const double & );
 const Matrix<T> & operator = ( const T & );

de ezt a fordító nem akarja megenni, merthogy ha T épp double, akkor nem tudja eldönteni, hogy a két függvény közül melyiket használja. Másrészt viszont nem tudom, hogy tudnám biztosítani, hogy az értékadás operátor mindig működjön double paraméterrel (komplex értékű mátrixot is lehet valós értékkel inicializálni elvégre).


SAdam 2009.X.05 04:49

(Válaszképp erre)

<blockquote> Na most a problémám már csak az, hogy hogyan tudok "feltételes fordítást" csinálni annak alapján, hogy milyen típus alapján készült a template? Magyarul, vagy egy rutinom (konkrétan, az a rutin, ami a mátrix sajátértékeit számolja), ami más algoritmust kellene, hogy futtasson akkor, ha a mátrix komplex, mint akkor, ha a mátrix valós. Első blikkre a dynamic_cast paranccsal próbáltam megoldani a dolgot (úgy, hogy az eljárás elején definiáltam egy T típusú változót, és tesztelem, hogy mivé tudom castolni, és ez alapján egy elágazás szerint csinálom a csinálnivalót), de ez egyrészt csúnya, másrészt valahogy nem akar működni, ugyanis a fordító már fordítási időben közli, hogy nem fog menni a castolás, ezért le sem fordítja a dolgot. Van erre valami szép módszer, hogy külön definiálni tudjam a Matrix<double> és a Matrix<__CLPK_complex> osztályok ugyanolyan nevű (legyen mondjuk getEigenValues()) metódusát? </blockquote>

UPDATE: Ezt a problémát közben sikerült megoldani, viszont fellépett helyette egy másik: sajnos ha szokásosan kiteszem egy külön fejléc file-ba a definíciókat, akkor a fordító nem találja a fordítandót, ezért muszáj voltam egy file-ba pakolni mindent. Így viszont az a hülye helyzet állt elő, hogy mivel a projektemben több file is include-olja a mátrixomat tartalmazó fejléc file-t, ezért gyönyörű duplicate symbol hibákat kapok linkeléskor. Tud valaki erre megoldást? (Amúgy fura, hogy ezzel ennyit bénázom, máskor általában fórumokon egész gyorsan fel lehet fogni dolgokat, de valahogy template-ügyben nem sikerül megtalálnom a megfelelő fórumokat, úgy tűnik...)

Tagek: