Această întrebare are deja răspunsuri aici :
Comentarii
Răspuns
În afară de indicatorul void *
care este acoperit în răspunsul lui Robert , a fost utilizată o tehnică de acest gen (Disclaimer: Memorie veche de 20 de ani):
#define WANTIMP #define TYPE int #include "collection.h" #undef TYPE #define TYPE string #include "collection.h" #undef TYPE int main() { Collection_int lstInt; Collection_string lstString; }
Unde am uitat exact magia preprocesatorului din collection.h
, dar a fost ceva de genul acesta:
class Collection_ ## TYPE { public: Collection_ ## TYPE () {} void Add(TYPE value); private: TYPE *list; size_t n; size_t a; } #ifdef WANTIMP void Collection_ ## TYPE ::Add(TYPE value) #endif
Comentarii
Răspuns
The mod tradițional de a implementa generice fără a avea generice (motivul pentru care au fost create șabloanele) este de a utiliza un pointer gol.
typedef struct Item{ void* data; } Item; typedef struct Node{ Item Item; struct Node* next; struct Node* previous; } Node;
În acest exemplu de cod, un arbore binar sau poate fi reprezentată o listă dublă. Deoarece item
încapsulează un pointer gol, orice tip de date poate fi stocat acolo. Desigur, ar trebui să cunoașteți tipul de date în timpul rulării, astfel încât să îl puteți arunca înapoi la un obiect utilizabil.
Comentarii
Răspuns
După cum au subliniat alte răspunsuri, puteți utiliza void*
pentru structuri de date generice.Pentru alte tipuri de polimorfism parametric, macro-urile preprocesorului au fost utilizate dacă ceva s-a repetat lot (ca de zeci de ori). Pentru a fi sincer, totuși, de cele mai multe ori pentru repetări moderate, oamenii tocmai au copiat și lipit, apoi au schimbat tipurile, deoarece există o mulțime de capcane cu macro-uri care le fac problematice.
Avem cu adevărat nevoie de un nume pentru conversația paradox blub , unde oamenilor le este greu să-și imagineze programarea într-un limbaj mai puțin expresiv, deoarece acest lucru apare foarte mult pe acest site. Dacă „nu ați folosit niciodată un limbaj cu modalități expresive de implementare a polimorfismului parametric, nu știți cu adevărat ce vă lipsește. Acceptați doar copierea și lipirea ca fiind oarecum enervant, dar necesar.
Există ineficiențe în limbile dvs. actuale de alegere, de care nici măcar nu știți încă. Peste douăzeci de ani oamenii se vor întreba cum i-ai eliminat. Răspunsul scurt este că nu știai că poți.
Comentarii
Răspuns
Îmi amintesc când gcc a fost livrat cu genclass
– un program care a luat ca intrare un set de tipuri de parametri ( de exemplu, cheie și valoare pentru o hartă) și un fișier de sintaxă special care a descris un tip parametrizat (să zicem, o hartă sau un vector) și a generat implementări C ++ valide cu tipurile de parametri completate.
Deci dacă avea nevoie de Map<int, string>
și Map<string, string>
(aceasta nu a fost sintaxa reală, să ne gândim la) pe care a trebuit să o rulați acel program de două ori pentru a genera ceva de genul map_string_string.h și map_int_string.h și apoi utilizați-le în codul dvs.
Consultați pagina de manual pentru genclass
și documentația din Biblioteca GNU C ++ 2.0 pentru mai multe detalii.
Răspuns
[Pentru PO: „Nu încerc să mă iau personal, ci vă conștientizez pe dvs. și pe ceilalți” de a vă gândi la logica întrebării ( s) a întrebat pe SE și în altă parte. Vă rugăm să nu luați acest lucru personal!]
Titlul întrebării este bun, dar limitați sever sfera răspunsurilor dvs., incluzând „… situații în care aveau nevoie de generarea codului în timp de compilare. „Multe răspunsuri bune la întrebarea despre cum se poate genera generarea codului în timp de compilare în C ++ fără șabloane există pe această pagină, dar pentru a răspunde la întrebarea pe care ați pus-o inițial:
Ce au făcut oamenii înainte de șabloane în C ++?
Răspunsul este, desigur, ei (noi) nu le-au folosit. Da, sunt limbă în obraz, dar detaliile întrebării din corp par (probabil exagerat) să presupună că toată lumea iubește șabloanele și că nu s-ar fi putut face niciodată codificare fără ele.
De exemplu, am finalizat multe proiecte de codificare în diferite limbi, fără a fi nevoie de generarea codului în timp de compilare și cred că și alții au făcut-o. Sigur, problema rezolvată de șabloane a fost o mâncărime suficient de mare încât cineva l-a zgâriat, dar scenariul propus de această întrebare a fost, în mare parte, inexistent.
Luați în considerare o întrebare similară la mașini:
Cum s-au mutat șoferii de la o treaptă de viteză la alta, folosind o metodă automată care a schimbat vitezele pentru dvs., înainte de a fi inventată transmisia automată?
Întrebarea este, desigur, o prostie. Întrebarea cum a făcut X o persoană înainte de inventarea lui X nu este „într-adevăr o întrebare validă. Răspunsul este, în general,„ nu am făcut-o și nu am ratat-o pentru că nu știam că va exista vreodată ”.Da, este ușor să vezi beneficiul după fapt, dar să presupunem că toată lumea stătea în picioare, dădea cu piciorul în călcâie, aștepta transmisia automată sau șabloanele C ++, nu este adevărat.
La întrebarea „cum au schimbat șoferii vitezele înainte de a fi inventată transmisia automată?” Se poate răspunde în mod rezonabil „manual” și acesta este tipul de răspunsuri pe care le primiți aici. Poate fi chiar tipul de întrebare pe care ați intenționat să o puneți.
Dar nu a fost cea pe care ați întrebat-o.
Deci:
Î: Cum au folosit oamenii șabloanele înainte de a fi inventate șabloanele?
R: Nu am făcut-o.
Î: Cum au folosit oamenii șabloanele înainte de a fi inventate șabloanele, când au avut nevoie să folosiți șabloane ?
A: Nu nu avem nevoie să le folosim. De ce să presupunem că le-am făcut? (De ce să presupunem că le facem?)
Î: Care sunt modalitățile alternative de a obține rezultatele oferite de șabloane?
R: Mai multe răspunsuri bune există mai sus.
Vă rog să vă gândiți la erori logice în postările dvs. înainte de a posta.
[Vă mulțumim! Vă rog, nu se intenționează niciun rău aici.]
Comentarii
Răspuns
Răspuns
Așa cum a spus deja Robert Harvey, tipul de date generic este un pointer gol.
Un exemplu din biblioteca C standard, cum se sortează o matrice de dublu cu un tip generic:
double *array = ...; int size = ...; qsort (array, size, sizeof (double), compare_doubles);
Unde compare_double
este definit ca:
int compare_doubles (const void *a, const void *b) { const double *da = (const double *) a; const double *db = (const double *) b; return (*da > *db) - (*da < *db); }
Semnătura qsort
este definită în stdlib.h:
void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *) );
Rețineți că nu există niciun tip verificarea la timpul compilării, nici măcar la timpul de rulare. Dacă sortați o listă de șiruri cu comparatorul de mai sus care se așteaptă la dubluri, va încerca cu bucurie să interpreteze reprezentarea binară a unui șir ca o dublă și să sorteze în consecință.
Răspuns
O modalitate de a face acest lucru este așa:
https://github.com/rgeminas/gp–/blob/master/src/scope/darray.h
#define DARRAY_DEFINE(name, type) DARRAY_TYPEDECL(name, type) DARRAY_IMPL(name, type) // This is one single long line #define DARRAY_TYPEDECL(name, type) \ typedef struct darray_##name \ { \ type* base; \ size_t allocated_mem; \ size_t length; \ } darray_##name; // This is also a single line #define DARRAY_IMPL(name, type) \ static darray_##name* darray_init_##name() \ { \ darray_##name* arr = (darray_##name*) malloc(sizeof(darray_##name)); \ arr->base = (type*) malloc(sizeof(type)); \ arr->length = 0; \ arr->allocated_mem = 1; \ return arr; \ }
Macrocomanda DARRAY_TYPEDECL creează în mod eficient o definiție struct (într-o singură linie), înlocuind name
cu numele pe care îl treceți și stocarea unui tablou din type
pe care îl treceți (name
este acolo, astfel încât să îl puteți concatena la numele struct de bază și încă mai au un identificator valid – darray_int * nu este un nume valid pentru o struct), în timp ce macrocomanda DARRAY_IMPL definește funcțiile care operează pe structura respectivă (în acest caz, ele sunt „marcate static doar astfel încât să numiți definiția o singură dată și nu separați totul).
Aceasta ar fi folosită ca:
#include "darray.h" // No types have been defined yet DARRAY_DEFINE(int_ptr, int*) // by this point, the type has been declared and its functions defined darray_int_ptr* darray = darray_int_ptr_init();
Răspuns
Cred că șabloanele se obișnuiesc mult ca modalitate de reutilizare a tipurilor de containere care au un o mulțime de valori algoritmice, cum ar fi matrice dinamice (vectori), hărți, arbori, etc. sortare etc.
Fără șabloane, în mod necesar, acestea conțin implementări sunt scrise într-un mod generic și li se oferă doar suficiente informații despre tipul necesar domeniului lor. De exemplu, cu un vector, au nevoie doar ca datele să fie „capabile” și trebuie să știe mărimea fiecărui articol.
Să spunem că aveți o clasă de containere numită Vector care face acest lucru. Se anulează *. Utilizarea simplistă a acestui lucru ar fi ca codul stratului de aplicație să facă o mulțime de casting. Deci, dacă gestionează obiecte Cat, trebuie să arunce Cat * în gol * și să revină peste tot. Litteringul codului de aplicație cu proiecte are probleme evidente.
Șabloanele rezolvă acest lucru.
O altă modalitate de a rezolva acest lucru este crearea unui tip de container personalizat pentru tipul pe care îl stocați în container. Deci, dacă aveți o clasă Cat, ați crea o clasă CatList derivată din Vector.Apoi suprasolicitați câteva metode pe care le utilizați, introducând versiuni care iau obiecte Cat în loc de void *. Așadar, veți supraîncărca metoda Vector :: Add (void *) cu Cat :: Add (Cat *), care intern transmite pur și simplu parametrul la Vector :: Add (). Apoi, în codul aplicației dvs., veți apela la versiunea suprasolicitată de Add atunci când treceți într-un obiect Cat și evitați astfel aruncarea. Pentru a fi corect, metoda Adăugare nu ar necesita o distribuție, deoarece un obiect Cat * se transformă în gol * fără o distribuție. Dar metoda de recuperare a unui element, cum ar fi supraîncărcarea indexului sau o metodă Get () ar fi.
Cealaltă abordare, singurul exemplu pe care mi-l amintesc de la începutul anilor 90, cu un cadru de aplicații mare, este de a utiliza un utilitar personalizat care creează aceste tipuri pe clase. Cred că MFC a făcut acest lucru. Au avut clase diferite pentru containere ca CStringArray, CRectArray, CDoulbeArray, CIntArray, etc. În loc să mențină codul duplicat, au făcut un anumit tip de meta-programare similară cu macrocomenzile, folosind un instrument extern care să genereze clasele. Au făcut instrumentul disponibil cu Visual C ++ în cazul în care cineva am vrut să-l folosesc – nu am făcut-o niciodată. Poate că ar fi trebuit să o fac. Dar la vremea respectivă, experții spuneau „subînțelesul sănătos al C ++” și „Nu aveți nevoie de șabloane”