Luettuani asiasta netistä olen ymmärtänyt, että mallin tyyppiä ei voi tarkistaa käännöksen aikana. Minulla on tällainen tilanne:
template <class T> class Luokka { ... std::vector <T> funktio () {...} }
Joten jos luokan käyttäjä tekee näin
Luokka <void> olio;
aiheuttaa se luonnollisesti lukuisan määrän käännöksenaikaisia virheitä. Onko siis täysin mahdotonta tarkistaa T esikääntäjällä ja tulostaa jotain tyyliin "T must not be void"? En keksi toista lähestymistapaa tähän, mutta uskon että sellainen on ja löytyy, jos tietää ja miettii tarpeeksi. Mitä mieltä olette?
No eikös ne käännösaikaiset virheet indikoi aika hyvin sen, että "T must not be void"? Yleisestikin ottaen tuntuu aika erikoiselta edes yrittää työntää voidia templatelle. Varmaan enemmän poikkeus sellainen template, joka tekee jotain järkevää voidinkin tapauksessa.
Harkitsin tuotakin. Jos olet kokeneena ammattilaisena sitä mieltä, tyydyn nykyiseen ratkaisuun :)
No C++ ei ole todellakaan vahvimpia alueitani...
Eikai sillä kielellä ole väliä, kun kyseessä on luokan käyttäjäystävällisyys :D
No siinä mielessä sillä voi olla jotain merkitystä, että jos esim. C#:ssa yritän antaa tyyppiparametriksi void, niin se sanoo:
Error 12 Keyword 'void' cannot be used in this context
Eli ainakaan C#:ssa koodaajan ei tarvitse mitenkään itse määritellä, että void ei ole mahdollinen.
Tuollaiset tarkastukset voi C++:n nykyversiossa hoitaa static_assertin avulla:
#include <type_traits> #include <vector> template<class T> class Luokka { static_assert(!std::is_void<T>::value, "Tyyppiparametri ei saa olla void."); std::vector<T> function() {} };
Jos käytät vanhaa C++-standardia, onnistuu sama homma Boostin avulla samaan tapaan:
#include <boost/static_assert.hpp> #include <boost/type_traits.hpp> ... BOOST_STATIC_ASSERT(!boost::is_void<T>::value);
Ihan mielenkiinnosta, niin onko kovinkin yleistä että void on järkevä tyyppiaparametri templatelle ja vaikka joku esimerkki sellaisesta?
Voit tehdä mallista erikoistuneen version (template specialization) kyseiselle parametrille. Toteutuksella ei tarvitse olla mitään tekemistä alkuperäisen mallin kanssa, vaan voit tehdä siihen ihan eri funktioita tai jättää sen tyhjäksi.
template <typename T> struct S { static T f() { return T(); } }; template <> struct S<void> { static float f() { return 1.23f; } }; #include <iostream> int main() { std::cout << S<int>::f() << std::endl; // int() == 0 std::cout << S<void>::f() << std::endl; // 1.23f }
Voit myös esitellä erikoistuneen version mutta jättää sen toteuttamatta.
template <typename T> struct S { // ... }; template <> struct S<void>; int main() { S<void> s; // error: aggregate ‘S<void> s’ has incomplete type and cannot be defined }
Täytynee lisätä näitä joskus oppaaseen.
Grez kirjoitti:
Ihan mielenkiinnosta, niin onko kovinkin yleistä että void on järkevä tyyppiaparametri templatelle ja vaikka joku esimerkki sellaisesta?
Ei ole kovin yleistä, mutta esimerkiksi osoitinluokkien kohdalla siinä voi olla järkeä, tai voisihan sitä käyttää ilmaisemaan, että jotain tietoa ei haluta tallentaa, tyyliin pituus_ja_paksuus<double, void>.
Kiitos Sisuaski ja Metabolix! Molemmilla hyvin mielenkiintoiset ratkaisut :)
Grez kirjoitti:
Ihan mielenkiinnosta, niin onko kovinkin yleistä että void on järkevä tyyppiaparametri templatelle ja vaikka joku esimerkki sellaisesta?
std::function<void, ...>
ja muut funktiowrapperit ovat ehkä käytännönläheisin esimerkki. Koodasin kerran C++-kirjaston Java Native Interfacen päälle, ja voidin palauttavat Java-metodit, joita kuvattiin C++-puolella Method<void, ...>
-tyyppisillä objekteilla, vaativat jonkin verran erikoiskäsittelyä.
Hienoa, nyt olen taas vähän viisaampi..
Jäin vielä miettimään, että jos tehdään kirjastoja yleiseen jakoon ja siellä on templateja jotka ei toimi void tyyppiparametrin kanssa niin onko yleensä tapana tehdä tuollaiset ilmoitukset/tarkistukset, vai lähteä siitä että jos koodaaja ei muuten tajua ettei void ole sopiva niin käännösaikaiset virheet on riittävä indikaattori?
Aihe on jo aika vanha, joten et voi enää vastata siihen.