9

In C++ 11, sembra come se fosse legale per inizializzare un std::map<std::string, int> come segue:Quali regole di linguaggio consentono a C++ 11 di dedurre che si tratta di una lista_iniziale di coppie?

std::map<std::string, int> myMap = { 
    { "One", 1 }, 
    { "Two", 2 }, 
    { "Three", 3 } 
}; 

Intuitivamente, questo ha un senso - l'inizializzazione brace chiusa è una lista di coppie di stringhe, e std::map<std::string, int>::value_type è std::pair<std::string, int> (possibilmente con alcune qualifiche const.

Tuttavia, non sono sicuro di aver capito come funziona la digitazione qui. Se eliminiamo la dichiarazione delle variabili qui e abbiamo solo l'inizializzatore racchiuso tra parentesi, il compilatore non lo saprebbe Stavo guardando uno std::initializer_list<std::pair<std::string, int>> perché non saprebbe che le coppie rinforzate sono ri presentato std::pair s. Pertanto, sembra che il compilatore stia differendo in qualche modo l'atto di assegnare un tipo all'inizializzatore racchiuso tra parentesi finché non ha abbastanza informazioni sul tipo dal costruttore std::map per comprendere che le parentesi graffe nidificate sono per coppie. Non ricordo nulla di simile accadendo in C++ 03; per quanto ne so, il tipo di espressione non dipende mai dal suo contesto.

Quali regole di linguaggio consentono a questo codice di compilare correttamente e al compilatore di determinare il tipo da utilizzare per l'elenco di inizializzazione? Sto sperando in risposte con riferimenti specifici alla specifica C++ 11, dal momento che è davvero interessante che funzioni!

Grazie!

+0

Gli elenchi di bretelle in C++ 03 erano già molto specifici per il contesto di inizializzazione. Non c'è alcuna differenza significativa qui da un aggregato annidato in C++ 03 (forse 'std :: pair myArray []') inizializzato usando le parentesi, salvo che il tipo non è direttamente chiamato, è dedotto da i costruttori disponibili. –

+0

BTW, gli operatori di conversione impliciti (membro 'operator T()') sono l'altro posto in cui il contesto determina il tipo di un'espressione (ed è usato per la risoluzione di sovraccarico). –

+2

'braced-init-list' non è un'espressione e non ha tipo. Non c'è deduzione, solo la risoluzione di sovraccarico tra i costruttori di inizializzatore-elenco (di cui c'è solo uno) – Cubbi

risposta

9

Nell'espressione

std::map<std::string, int> myMap = { 
    { "One", 1 }, 
    { "Two", 2 }, 
    { "Three", 3 } 
}; 

sul lato destro si hanno un rinforzato-init-list dove ogni elemento è anche un-init-list rinforzato. La prima cosa che succede è che viene considerato il costruttore dell'elenco inizializzatore di std::map.

map(initializer_list<value_type>, 
    const Compare& = Compare(), 
    const Allocator& = Allocator()); 

map<K, V>::value_type è un typedef per pair<const K, V>, in questo caso pair<const string, int>. Gli elenchi di parentesi inseriti interni possono essere convertiti con successo in map::value_type poiché std::pair dispone di un costruttore che prende i riferimenti ai suoi due tipi costitutivi e std::string ha un costruttore di conversione implicita che accetta uno char const *.

Pertanto, il costruttore dell'elenco di inizializzazione di std::map è valido e la costruzione può avvenire dagli elenchi di parentesi inseriti nested.

Lo standardese richiesta sia presente in §13.3.1.7/1 [over.match.list]

Quando gli oggetti di tipo non-aggregato classe T sono elenco inizializzata (8.5.4), la risoluzione di sovraccarico seleziona il costruttore in due fasi:
- Inizialmente, le funzioni candidate sono i costruttori di inizializzatore-elenco (8.5.4) della classe T e l'elenco di argomenti è costituito dall'elenco di inizializzatore come un singolo argomento.
- Se non viene trovato alcun costruttore di inizializzatore di inizializzazione valido, viene eseguita nuovamente la risoluzione di sovraccarico, dove le funzioni candidate sono tutti i costruttori della classe T e l'elenco di argomenti è costituito dagli elementi dell'elenco di inizializzazione.

Il primo proiettile è ciò che provoca il initializer_list costruttore map da selezionare per l'esterno rinforzato-init-list, mentre il secondo risultati proiettile nella selezione della corretta pair costruttore della interno rinforzato-INIT- elenchi.

+0

Meraviglioso, grazie! – templatetypedef

2

Questo è inizializzazione lista. Le regole si trovano in §8.5.4 [dcl.init.list]/p3 dello standard:

List-inizializzazione di un oggetto o di riferimento di tipo T è definito come segue:

  • Se l'elenco di inizializzazione non ha elementi e T è un tipo di classe con un costruttore predefinito, l'oggetto viene inizializzato in base al valore.
  • In caso contrario, se T è un aggregato, viene eseguita l'inizializzazione di aggregazione (8.5.1). [Esempio omessa]
  • Altrimenti, se T è una specializzazione di std::initializer_list<E>, un oggetto initializer_list è costruito come descritto di seguito e utilizzato per inizializzare l'oggetto secondo le regole di inizializzazione di un oggetto da una classe dello stesso tipo (8.5).
  • Altrimenti, se T è un tipo di classe, vengono considerati i costruttori. I costruttori applicabili sono elencati e il migliore viene scelto tramite la risoluzione di sovraccarico (13.3, 13.3.1.7). Se per convertire uno qualsiasi degli argomenti è richiesta una restringente conversione (vedi sotto), il programma è mal formato.
  • [esempio e il resto delle regole omessi]

Nota che sovraccaricano risoluzione preferiranno std::initializer_list costruttori in questi casi (§13.3.1.7 [over.match.list]).

Così, quando il compilatore vede un elenco controventata utilizzato per inizializzare un oggetto di un non-aggregato, non tipo std::initializer_list classe, eseguirà risoluzione di sovraccarico per selezionare il costruttore appropriato, preferendo il costruttore initializer_list se esiste una valida (come fa per std::map).

Problemi correlati