2009-12-07 12 views
5

Sono un po 'curioso di alcune delle nuove funzionalità di C++ 0x. In particolare range-based for loops e initializer lists. Entrambe le funzionalità richiedono una classe definita dall'utente per funzionare correttamente.C++ 0x, ganci del compilatore e caratteristiche dei linguaggi hard codificati

Mi sono imbattuto in this post e mentre la risposta in alto era utile. Non so se è completamente corretto (Probabilmente sono solo completamente frainteso, vedi 3 ° commento sulla prima risposta). Secondo il current specifications per le liste di inizializzazione, l'intestazione definisce un tipo:

template<class E> class initializer_list { 
public: 
    initializer_list(); 

    size_t size() const; // number of elements 
    const E* begin() const; // first element 
    const E* end() const; // one past the last element 
}; 

Si può vedere questo nelle specifiche, semplicemente Ctrl + F 'classe initializer_list'.

Affinché = {1,2,3} per essere implicitamente fuso nella classe initializer_list, il compilatore deve avere una certa conoscenza della relazione tra {} e initializer_list. Non c'è costruttore che riceve qualcosa, quindi l'inizializzatore_list per quanto posso dire è un wrapper che viene associato a qualsiasi cosa il compilatore stia effettivamente generando.

È lo stesso con la for(:) ciclo, che richiede anche un tipo definito dall'utente lavorare (se secondo le specifiche, aggiornati non richiede alcun codice per array e liste di inizializzazione. Ma elenchi di inizializzazione richiedono <initializer_list>, quindi è un requisito del codice definito dall'utente per proxy).

Sto fraintendendo completamente come funziona qui? Non mi sbaglio nel pensare che queste nuove funzionalità si basino in maniera estremamente pesante sul codice utente. Sembra come se le funzionalità fossero semicipite, e invece di costruire l'intera funzione nel compilatore, è fatta a metà dal compilatore e metà inclusa in include. Qual è la ragione di questo?

Modifica: Ho digitato "fare molto affidamento sul codice del compilatore" e non "fare molto affidamento sul codice utente". Che penso abbia completamente gettato via la mia domanda. La mia confusione non riguarda le nuove funzionalità incorporate nel compilatore, sono cose che sono incorporate nel compilatore che si basano sul codice utente.

risposta

3

non sbaglio nel pensare che queste nuove funzioni fanno infatti si basano molto pesantemente sul codice compilatore

Essi si basano molto sul compilatore. Indipendentemente dal fatto che sia necessario includere un'intestazione o meno, il fatto è che in entrambi i casi la sintassi sarebbe un errore di analisi con i compilatori di oggi. Il for (:) non lo fa abbastanza in forma in oggi standard, dove il costrutto unica consentita è for(;;)

Ci si sente come se le caratteristiche sono cotto a metà, e invece di costruire l'intera funzione nel compilatore, è in fase di metà-fatto dal compilatore e mezzo incluso. Qual è la ragione di questo?

Il supporto deve essere implementato nel compilatore, ma è necessario includere un'intestazione di sistema affinché funzioni. Questo può servire a un paio di scopi, nel caso di liste di inizializzazione, porta il tipo (interfaccia al supporto del compilatore) in ambito per l'utente in modo che tu possa avere un modo di usarlo (pensa a come va_args è in C). Nel caso del range-based for (che è solo zucchero sintattico) è necessario portare Range in modo che il compilatore possa eseguire la sua magia. Si noti che la norma definisce for (for-range-declaration : expression) statement equivalenti a ([6.5.4]/1 del progetto):

{ 
    auto && __range = (expression); 
    for (auto __begin = std::Range<_RangeT>::begin(__range), 
     __end = std::Range<_RangeT>::end(__range); 
     __begin != __end; 
     ++__begin) { 
     for-range-declaration = *__begin; 
     statement 
    } 
} 

Se si desidera utilizzare solo su array e contenitori STL che potrebbero essere attuate senza il concetto Range (non nel senso C++ 0x), ma se si desidera estendere la sintassi in classi definite dall'utente (i propri contenitori) il compilatore può facilmente dipendere dal modello esistente Range (con la propria eventuale specializzazione). Il meccanismo di dipendenza da un modello definito è equivalente a richiedere un'interfaccia statica sul contenitore.

La maggior parte degli altri linguaggi sono andati nella direzione di richiedere un'interfaccia regolare (ad esempio Contenitore, ...) e utilizzare il polimorfismo di runtime su quello. Se ciò dovesse essere fatto in C++, l'intero STL dovrebbe passare attraverso un importante refactoring poiché i contenitori STL non condividono una base o un'interfaccia comune e non sono preparati per essere utilizzati polimorficamente.

In caso affermativo, lo standard corrente non sarà stellato quando si spegne.

+0

Posso capire il requisito per il costrutto for (:), senza il compilatore che richiede un'iterazione differente del contenitore sarebbe impossibile. Ma for (:) non richiede che un tipo funzioni, solo se intendi estenderlo. Quale per me si sente simile al nuovo meccanismo di sovraccarico di posizionamento. Tuttavia, non vedo ancora il motivo dell'elenco initializer_type. = {1,2,3,4,5} a un letterale di array, passato a un operatore void {} (int arr [], 5); sembra più pulito e non richiede sistemi di inclusione. Se l'utente vuole inserirla in un contenitore, può farlo in quella funzione. – Dave

+0

Proprio come hai menzionato va_args, le funzioni e le macro sono basate su una funzione del compilatore. Non otterrai un errore del compilatore per non includere va_args, potresti scrivere il tuo. Allo stesso modo C++ 0x []() {} lambda non richiede std :: function, potresti ancora scriverne uno tuo. {} d'altra parte richiede l'esistenza di un nome di classe specifico, che andrà in crash se non esiste. E non dovrebbe essere forzato, immagino di essere solo un "nerd furioso", ma molto di quello che vedo nello standard C++ 0x sembra essere sempre più alieno di minuto in minuto. – Dave

+0

È un po 'più complicato di così. Il compilatore e gli implementatori di STL non devono essere uguali. Microsoft utilizza l'implementazione di Dinkunware STL, ma è possibile utilizzare STLPort o qualsiasi altro. Le classi STL non sono definite al 100% e inchiodate e un implementatore STL può decidere di aggiungere nuovi parametri del modello purché abbiano un valore predefinito e possano quindi essere utilizzati nel codice utente che dipende e utilizza solo quelli dello standard. Ciò significa che il compilatore non può realmente abbinare facilmente i contenitori STL da solo. –

1

È solo zucchero sintassi. Il compilatore espanderà i costrutti sintattici dati in espressioni C++ equivalenti che fanno riferimento direttamente ai tipi di simboli standard/nomi.

Questo non è il solo accoppiamento forte che i compilatori C++ moderni hanno tra il loro linguaggio e il "mondo esterno". Ad esempio, extern "C" è un po 'un hack del linguaggio per ospitare il modello di collegamento di C. Le modalità orientate alla lingua di dichiarare lo storage locale su thread dipendono implicitamente da un sacco di hackery RTL per funzionare.

Oppure guardare C. Come si accede agli argomenti passati tramite ...? Devi fare affidamento sulla libreria standard; ma quello usa la magia che ha una dipendenza molto dura da quanto esattamente il compilatore C stende i frame dello stack.

UPDATE:

Se non altro, l'approccio C++ ha preso qui è più nello spirito di C++ che l'alternativa - che sarebbe quello di aggiungere un intrinseco collezione o un intervallo di tipo , cotto in al linguaggio. Invece, viene eseguito tramite un tipo di intervallo definito dal fornitore. Davvero non lo vedo molto diverso dagli argomenti variadici, che sono ugualmente inutili senza le macro accessorie definite dal fornitore.

+0

... e la "C" esterna sono caratteristiche linguistiche, tuttavia, non è necessario scrivere la classe che si espande ... in argomenti, basta usare ... per indicare cosa deve fare il compilatore. {} d'altra parte richiede una classe chiamata initializer_list, se non esiste non funzionerà, si potrebbe compilare la funzione (int a, ...) senza alcun tipo. – Dave

+0

Dave, thread_local non funzionerà senza intestazioni nella RTL, né sarà supportato dal dispatching delle eccezioni. Penso che tu abbia appena vissuto una vita riparata :) - il compilatore sta facendo più lavoro con la cooperazione della RTL di quanto tu non ne sia a conoscenza, e tu l'hai appena dato per scontato. Ora ecco che arriva un'altra funzionalità ('{}') che ha bisogno del supporto RTL ('std :: initializer_list') e vieni sorpreso! –

+0

No, naturalmente, ma l'identificatore di thread_local è proprio questo, un identificatore che specifica cosa fare. Non ho alcun problema con il compilatore che fa il lavoro in background. Il tuo esempio di ... per esempio, la funzione ... lingua non ha bisogno di va_list per funzionare. va_list è costruito attorno a una funzionalità del compilatore, diavolo potresti scrivere tu stesso. std :: initializer d'altra parte richiede che il codice esista per funzionare, è possibile compilare function (int, ...) con no includes. E, naturalmente, in che modo {} ha bisogno del supporto RTL? – Dave

Problemi correlati