Perché std :: initializer_list non è un linguaggio integrato?


95

Perché non è std::initializer_listintegrato un linguaggio di base?

Mi sembra che sia una caratteristica abbastanza importante di C ++ 11 e tuttavia non ha una propria parola chiave riservata (o qualcosa di simile).

Invece, initializer_listè solo una classe modello dalla libreria standard che ha una speciale mappatura implicita dalla nuova sintassi con parentesi graffe-init-list {...} gestita dal compilatore.

A prima vista, questa soluzione è piuttosto hacker .

È questo il modo in cui verranno implementate le nuove aggiunte al linguaggio C ++: dai ruoli impliciti di alcune classi modello e non dal linguaggio principale ?


Si prega di considerare questi esempi:

   widget<int> w = {1,2,3}; //this is how we want to use a class

perché è stata scelta una nuova classe:

   widget( std::initializer_list<T> init )

invece di usare qualcosa di simile a una qualsiasi di queste idee:

   widget( T[] init, int length )  // (1)
   widget( T... init )             // (2)
   widget( std::vector<T> init )   // (3)
  1. un array classico, potresti probabilmente aggiungere constqua e là
  2. tre punti esistono già nella lingua (var-args, ora variadic templates), perché non riutilizzare la sintassi (e farla sentire incorporata )
  3. solo un contenitore esistente, potrebbe aggiungere conste&

Tutti loro fanno già parte della lingua. Ho scritto solo le mie prime 3 idee, sono sicuro che ci sono molti altri approcci.


26
Il comitato per gli standard odia aggiungere nuove parole chiave!
Alex Chamberlain

11
Questo lo capisco, ma ci sono molte possibilità su come estendere la lingua (la parola chiave era solo un esempio )
emesx

10
std::array<T>non fa più "parte della lingua" di std::initializer_list<T>. E questi non sono quasi gli unici componenti di libreria su cui si basa il linguaggio. See new/ delete, type_info, vari tipi di eccezione, size_te così via
bames53

6
@ Elmes: l'avrei suggerito const T(*)[N], perché si comporta in modo molto simile a come std::initializer_listfunziona.
Mooing Duck

1
Questo spiega perché std::arrayo un array di dimensioni statiche sono alternative meno desiderabili.
boicottaggio

Risposte:


48

C'erano già esempi di funzionalità del linguaggio "principali" che restituivano tipi definiti nello stdspazio dei nomi. typeidritorna std::type_infoe (allungando forse un punto) sizeofritorna std::size_t.

Nel primo caso, è già necessario includere un'intestazione standard per utilizzare questa cosiddetta funzione "linguaggio di base".

Ora, per gli elenchi di inizializzatori, accade che non sia necessaria alcuna parola chiave per generare l'oggetto, la sintassi è tra parentesi graffe sensibili al contesto. A parte questo, è lo stesso di type_info. Personalmente non credo che l'assenza di una parola chiave lo renda "più hacky". Un po 'più sorprendente, forse, ma ricorda che l'obiettivo era quello di consentire la stessa sintassi dell'inizializzatore con parentesi graffe già consentita per gli aggregati.

Quindi sì, probabilmente in futuro puoi aspettarti di più da questo principio di progettazione:

  • se si verificano più occasioni in cui è possibile introdurre nuove funzionalità senza nuove parole chiave, il comitato le prenderà.
  • se le nuove funzionalità richiedono tipi complessi, tali tipi verranno inseriti stdanziché come incorporati.

Quindi:

  • se una nuova funzionalità richiede un tipo complesso e può essere introdotta senza nuove parole chiave, otterrai ciò che hai qui, ovvero la sintassi del "linguaggio principale" senza nuove parole chiave e che utilizza i tipi di libreria da std.

Quello che si riduce, credo, è che non esiste una divisione assoluta in C ++ tra il "linguaggio di base" e le librerie standard. Sono capitoli diversi nello standard, ma ognuno fa riferimento all'altro, ed è sempre stato così.

C'è un altro approccio in C ++ 11, ovvero che i lambda introducano oggetti che hanno tipi anonimi generati dal compilatore. Poiché non hanno nomi, non sono affatto in uno spazio dei nomi, certamente non in std. Questo non è un approccio adatto per gli elenchi di inizializzatori, tuttavia, perché usi il nome del tipo quando scrivi il costruttore che ne accetta uno.


1
Mi sembra che questa divisione non sia possibile (mailny?) A causa di tali ruoli impliciti dei tipi. type_infoe size_tsono buoni argomenti .. beh size_tè solo un typedef .. quindi saltiamo questo. La differenza tra type_infoe initializer_listè che il primo è il risultato di un operatore esplicito e il secondo di un'azione implicita del compilatore. Mi sembra anche che initializer_list potrebbe essere sostituito con dei contenitori già esistenti .. o ancora meglio: qualsiasi che l'utente dichiari come tipo di argomento!
emesx

4
... o potrebbe essere la semplice ragione per cui se hai scritto un costruttore per vectorche prende un, arraypotresti costruire un vettore da qualsiasi array del tipo giusto, non solo quello generato dalla sintassi dell'elenco di inizializzatori. Non sono sicuro che sarebbe una cosa negativa costruire contenitori da qualsiasi array, ma non è l'intento del comitato nell'introdurre la nuova sintassi.
Steve Jessop

2
@Christian: No, std::arraynon ha nemmeno i costruttori. Il std::arraycaso è semplicemente un'inizializzazione aggregata. Inoltre, ti invito a unirti a me nella chat room di Lounge <C ++>, poiché questa discussione sta diventando un po 'lunga.
Xeo

3
@ChristianRau: Xeo significa che gli elementi vengono copiati quando viene creato l'elenco degli inizializzatori. La copia di un elenco di inizializzatori non copia gli elementi contenuti.
Mooing Duck

2
L'inizializzazione di @Christian List non implica initializer_list. Possono essere molte cose, inclusa una buona inizializzazione diretta o aggregata. Nessuno di questi coinvolge initializer_list (e alcuni semplicemente non possono funzionare in questo modo).
R. Martinho Fernandes

42

Il Comitato per gli standard C ++ sembra preferire non aggiungere nuove parole chiave, probabilmente perché ciò aumenta il rischio di rompere il codice esistente (il codice legacy potrebbe usare quella parola chiave come nome di una variabile, una classe o qualsiasi altra cosa).

Inoltre, mi sembra che definire std::initializer_listun contenitore basato su modelli sia una scelta piuttosto elegante: se fosse una parola chiave, come accederesti al suo tipo sottostante? Come lo itereresti? Avresti bisogno anche di un gruppo di nuovi operatori e questo ti costringerebbe a ricordare più nomi e più parole chiave per fare le stesse cose che puoi fare con i contenitori standard.

Trattare un std::initializer_listcome qualsiasi altro contenitore ti dà l'opportunità di scrivere codice generico che funzioni con qualsiasi di queste cose.

AGGIORNARE:

Allora perché introdurre un nuovo tipo, invece di utilizzare una combinazione di esistente? (dai commenti)

Per cominciare, tutti gli altri contenitori hanno metodi per aggiungere, rimuovere e posizionare elementi, che non sono desiderabili per una raccolta generata dal compilatore. L'unica eccezione è std::array<>che racchiude un array in stile C di dimensione fissa e quindi rimarrebbe l'unico candidato ragionevole.

Tuttavia, come sottolinea correttamente Nicol Bolas nei commenti, un'altra differenza fondamentale tra std::initializer_liste tutti gli altri contenitori standard (incluso std::array<>) è che questi ultimi hanno semantica di valore , mentre std::initializer_listha semantica di riferimento . Copiare un std::initializer_list, per esempio, non causerà una copia degli elementi che contiene.

Inoltre (ancora una volta, per gentile concessione di Nicol Bolas), avere un contenitore speciale per gli elenchi di inizializzazione delle parentesi graffe consente il sovraccarico sul modo in cui l'utente sta eseguendo l'inizializzazione.


4
Allora perché introdurre un nuovo tipo, invece di utilizzare una combinazione di esistente?
emesx

3
@elmes: in realtà è più simile std::array. Ma std::arrayalloca memoria mentre std::initializaer_listavvolge un array in fase di compilazione. Considerala come la differenza tra char s[] = "array";e char *s = "initializer_list";.
rodrigo

2
E essendo un tipo normale , il sovraccarico, la specializzazione del modello, la decorazione del nome e simili non sono problemi.
rodrigo

2
@rodrigo: std::arraynon alloca memoria, è semplice T arr[N];, la stessa cosa che sta supportando std::initializer_list.
Xeo

6
@Xeo: T arr[N] alloca la memoria, forse non nell'heap dinamico ma altrove ... Così fa std::array. Tuttavia un non vuoto initializer_listnon può essere costruito dall'utente, quindi ovviamente non può allocare memoria.
rodrigo

6

Questa non è una novità. Ad esempio, for (i : some_container)si basa sull'esistenza di metodi specifici o funzioni autonome in some_containerclasse. C # si affida ancora di più alle sue librerie .NET. In realtà, penso che questa sia una soluzione abbastanza elegante, perché puoi rendere le tue classi compatibili con alcune strutture linguistiche senza complicare la specifica del linguaggio.


2
metodi in classe o autonomi begine endmetodi. Questo è un po 'diverso IMO.
emesx

3
È? Di nuovo hai un costrutto di linguaggio puro che si basa su una costruzione specifica del tuo codice. Potrebbe anche essere stato fatto introducendo una nuova parola chiave, ad esempioiterable class MyClass { };
Spook

ma puoi posizionare i metodi dove vuoi, implementarli come vuoi .. C'è qualche somiglianza, sono d'accordo! Questa domanda riguarda initializer_listperò
emesx

4

Questa non è davvero una novità e come molti hanno sottolineato, questa pratica era presente in C ++ ed è presente, diciamo, in C #.

Andrei Alexandrescu ha menzionato un buon punto a riguardo: potresti pensarlo come una parte dell'immaginario spazio dei nomi "core", quindi avrà più senso.

Quindi, in realtà è qualcosa di simile a: core::initializer_list, core::size_t, core::begin(), core::end()e così via. Questa è solo una sfortunata coincidenza che lo stdspazio dei nomi abbia alcuni costrutti del linguaggio di base al suo interno.


2

Non solo può funzionare completamente nella libreria standard. L'inclusione nella libreria standard non significa che il compilatore non possa giocare trucchi intelligenti.

Anche se potrebbe non essere in grado di farlo in tutti i casi, potrebbe benissimo dire: questo tipo è ben noto, o un tipo semplice, ignoriamo initializer_liste abbiamo solo un'immagine di memoria di quale dovrebbe essere il valore inizializzato.

In altre parole int i {5};può essere equivalente int i(5);o int i=5;o addirittura intwrapper iw {5};Dove intwrapperè un semplice involucro classe sopra un int con un costruttore banale prendendo uninitializer_list


Abbiamo esempi riproducibili di compilatori che suonano effettivamente "trucchi intelligenti" come questo? È un po 'ragionevole ragionare come se , ma mi piacerebbe vedere prove.
underscore_d

L'idea di ottimizzare i compilatori è che il compilatore può trasformare il codice in qualsiasi codice equivalente. Il C ++ in particolare si basa sull'ottimizzazione per astrazioni "libere". L'idea di sostituire il codice dalla libreria standard è comune (guarda l'elenco integrato di gcc gcc.gnu.org/onlinedocs/gcc/Other-Builtins.html ).
Paul de Vrieze

In effetti, la tua idea che int i {5}coinvolge qualcuno std::initializer_listè sbagliata. intnon ha alcun costruttore std::initializer_list, quindi 5viene usato direttamente per costruirlo. Quindi l'esempio principale è irrilevante; semplicemente non c'è ottimizzazione da fare. Oltre a ciò, poiché std::initializer_listimplica che il compilatore crei e invii tramite proxy un array 'immaginario', immagino possa favorire l'ottimizzazione, ma questa è la parte 'magica' nel compilatore, quindi è separato dal fatto che l'ottimizzatore in generale possa fare qualcosa di intelligente con il grazioso oggetto opaco contenente 2 iteratori che risultano
underscore_d

1

Non fa parte del linguaggio di base perché può essere implementato interamente nella libreria, solo riga operator newe operator delete. Quale vantaggio ci sarebbe nel rendere i compilatori più complicati da compilare?

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.