Minulla on määritelty luokka:
class Buffer { public: //... template<typename T> bool write(const T& variable); template<typename T> bool read(T& variable); //... };
Sitten käytän sitä tähän tapaan(toisessa tiedostossa):
Buffer buffer; buffer.write(10); // undefined reference to `bool Buffer::write<int>(int const&)
Miten saisin kääntäjän luomaan tarvittavat versiot noista funktioista? Vai onko tämä sellainen asia, johon mallit eivät taivu ja joudun tekemään omat funktiot kaikille tietotyypeille?
Mainitaan vielä epäselvyyksien välttämiseksi, että homma toimii kyllä niillä tyypeillä, joita luokka käyttää sisäisesti.
Myös kaikista kummallisuuksista sopii huomauttaa :)
Ongelma johtuu siitä, että itse mallia ei käännöksen jälkeen ole, vaan on vain siitä luotuja funktioita.
Helpoin ratkaisu on tehdä noista inline-funktioita eli sisällyttää toteutuskin otsikkotiedostoon. Ohjelman koko ja käännösaika kasvavat hieman ainakin, jos funktio on iso, mutta toimiipa varmasti kaikilla tyypeillä.
Toinen vaihtoehto on erikseen määrätä tehtäväksi kaikki tarvittavat versiot. Tällöin pitää etukäteen tietää, mitä versioita muualla koodissa käytetään.
// C.hpp: esittely struct C { template <typename T> void f(T t); }; // C.cpp: toteutus ja mallin instantiaatio tyypeille int ja float. template <typename T> void C::f(T t) { // ... } template void C::f<int>(int t); template void C::f<float>(float t);
Tässä tapauksessa voi olla hyvä varmuuden vuoksi tehdä itse mallista yksityinen ja tarjota julkisesti käyttöön inline-wrapperit, jotka kutsuvat mallia. Näin vältytään ikäviltä yllätyksiltä.
struct C { void f(int t) { return _f(t); } void f(float t) { return _f(t); } private: template <typename T> void _f(T t); }; // Loput kuten äskenkin, tietysti funktiona _f eikä f.
Luulisin, että tuo "undefined reference..." tulee siksi, koska templateja ei käännetä object-koodiksi (pois lukien specializationit). Näin ollen nuo täytyy olla "header only" -tyyppisiä, muutoin käy köpelösti.
Jos välttämättä haluat siirtää noiden implementationin erilliseen .cpp -tiedostoon, täytyy sun tehdä erillinen header safeguard myös .cpp tiedostolle ja lisätä se .h tiedoston loppuun includella.
Viimeinkin pääsin perehtymään asiaan ja vinkkienne myötä keksin siirtää mallien toteutukset otsikkotiedostoon. Nyt kääntäjä osaa luoda funktiot tarpeen mukaan.
class Buffer { public: //... template <typename T> bool Buffer::write(const T& variable) { // Toteutus... } template <typename T> bool Buffer::read(T& variable) { // Toteutus... } //... };
Inline funktioiden kanssa tulisi ymmärtääkseni ongelmaksi viittausparametrit.
Kiitokset!
Nuohan nimenomaan ovat luokan inline-funktioita. Se, kirjoitatko ne noin vai luokan ulkopuolelle inline-sanan kanssa, on aivan makuasia.
struct C { void f() { } };
struct C { void f(); }; inline void C::f() { }
Jahas :) En ole aikaisemmin perehtynyt inline-funktioihin(vaikka olen niitä näemmä sitten käyttänyt) ja ymmärsin ne hieman väärin pikaisella googletuksella.
Nyt asian pitäisi olla selvä ja homma toimii.
Kiitokset oikaisusta :)
Metabolix kirjoitti:
Nuohan nimenomaan ovat luokan inline-funktioita. Se, kirjoitatko ne noin vai luokan ulkopuolelle inline-sanan kanssa, on aivan makuasia.
struct C { void f() { } };struct C { void f(); }; inline void C::f() { }
Parempi käytäntö on tosin kirjoittaa inline-funktiot luokan ulkopuolelle. Mikäli koodin kirjoittaja kirjoitaa inline-metodin suoraan luokan sisälle on se hankalampaa kolmannelle osapuolelle lukea sitä, sillä se sekoittaa keskenään asiat "mitä" ja "kuinka".
Kaiken kukkuraksi, olisi ehkä parasta tehdä asia näin:
class Foo { public: void Bar(); ... }; inline void Foo::Bar() { .... }
Tähän perusteluksi se, että Sinun päätös siitä, onko metodi inline-tyyppinen vai ei on puhtaasi toteutuksellinen kysymys eikä muuta luokan ulkopuolisen havaitsian käsitystä funktiosta (sen tarkoitus).
Tämän hetkinen toteutukseni on seuraavanlainen:
// file.hpp class Foo { public: template <typename T> void Bar(T& t); }; template <typename T> void Bar::Bar(T& t) { }
Eli toteutus on otsikkotiedostossa ja ilman inline-määrettä.
Homma toimii, kuten haluankin. Tällöinhän kyseessä ei ole inline-funktio?
Tietysti toteutuksen sijoittaminen otsikkotiedostoon on vähän kyseenalaista...
Jos homma toimii, anna homman olla. :)
Mainitaan nyt vielä, että ongelma ratkeaisi standardin mukaan export-sanalla, mutta mm. GCC ilmoittaa tylysti, ettei tue kyseistä ominaisuutta. Kunnollinen (tehokas) tuki tälle olisikin melkoinen temppu, kun tuotettuun ohjelmaan (tai esimerkiksi DLL-tiedostoon) pitäisi käytännössä sisällyttää oma kääntäjä.
Aihe on jo aika vanha, joten et voi enää vastata siihen.