2012-12-30 10 views
10

Il seguente codice non viene compilato in GCC 4.7.2 o 3.2 Clang:Impossibile copiare una std :: vector <std :: function <void()>> utilizzando l'inizializzazione uniforme. È corretto?

#include <vector> 
#include <functional> 

int main() 
{ 
    std::vector<std::function<void()>> a; 
    std::vector<std::function<void()>> b{a}; 
} 

Il problema è che il compilatore cercherà di creare b utilizzando un initializer_list, quando chiaramente dovrebbe essere solo chiamando il costruttore di copia. Tuttavia questo sembra essere il comportamento desiderato perché lo standard dice che i costruttori di initializer_list dovrebbero avere la precedenza.

Questo codice funzionerebbe bene per altri std :: vector, ma per una funzione std :: il compilatore non può sapere se si desidera il costruttore initializer_list o un altro.

Non sembra che ci sia un modo per aggirarlo, e se questo è il caso, allora non si può mai usare l'inizializzazione uniforme nel codice di codice. Quale sarebbe una vergogna gigante.

Visual Studio (CTP di novembre 2012) non si lamenta di ciò. Ma il supporto di initializer_list non è molto buono lì al momento, quindi potrebbe essere un bug.

risposta

10

Questo è LWG 2132 che non è ancora un rapporto sui difetti ma esiste un chiaro consenso (e esperienza di implementazione) per risolverlo. Lo standard dice che il costruttore di std::function accetta qualsiasi tipo, quindi, poiché un costruttore di inizializzatore-lista è sempre preferito ad altri costruttori se è possibile, il codice prova a costruire un vettore da un std::initializer_list<std::function<void()>> con un singolo elemento inizializzato dall'oggetto a. Ciò causa quindi un errore perché, sebbene sia possibile creare un std::function<void()> da a, l'oggetto risultante non è richiamabile.

In altre parole, il problema è che std::function ha un costruttore di modelli non vincolato che consente la conversione da qualsiasi tipo. Ciò causa un problema nel tuo caso perché i costruttori di inizializzatore-elenco sono preferiti ad altri costruttori se fattibile, e il costruttore non vincolato function significa che è sempre possibile creare un initializer_list<function<void()>> da qualsiasi tipo, quindi un costruttore di inizializzatore-elenco è sempre valido.

La risoluzione proposta a 2132 impedisce costruendo un std::function da un tipo non richiamabile, quindi il costruttore inizializzatore-elenco non è praticabile e il costruttore vector copia è chiamato invece. I implemented that resolution for GCC 4.8, ed è già implementato anche nella libreria libC++ di Clang.

+0

Grazie per la risposta. Il collegamento LWG 2132 parla in realtà di un problema simile ma diverso, così come è possibile risolvere anche questo problema. Significa che il problema verrà risolto solo per std :: function, non per altri tipi con costruttori basati su modelli. Penso che la mia nuova regola per l'inizializzazione uniforme sia "usarla ovunque sia possibile, tranne se l'oggetto ha un costruttore initializer_list, quindi usarlo solo se si vuole quel costruttore."Il che significa anche che non è possibile utilizzarlo in codice. –

+0

Preferisco la regola" non scrivere modelli di costruttori non vincolati che consentono la conversione implicita da qualsiasi tipo. "Se non crei tipi come quelli allora hai vinto" t causano problemi a chi cerca di usare il tuo tipo con classi che hanno costruttori di inizializzatori: la libreria standard non ha seguito quella regola, ma è stata risolta per 'std :: function'. –

5

Non vedo alcun motivo per cui questo non dovrebbe essere compilato e sia gcc (versione 4.8.0 20121111) che clang (versione 3.3 (trunk 171007)) compilano il codice. Ciò detto, "inizializzazione uniforme" è tutt'altro che uniforme: ci sono sicuramente casi in cui non è possibile utilizzare le parentesi quando si richiama un costruttore.

+0

g ++ 4.7.2 ('4.7.2-5ubuntu1') non compila il codice. Messaggio di errore del compilatore molto strano: http://pastebin.com/b1mcbYRq – leemes

+1

@leemes poiché la domanda si apre con "Il seguente codice non viene compilato in GCC 4.7.2 o Clang 3.2:", sto assumendo che l'OP lo sappia (ed è probabilmente la ragione della domanda in primo luogo). – WhozCraig

+0

@WhozCraig sì, ma non ha fornito il messaggio del compilatore. Questo è il motivo per cui ho postato questo commento in primo luogo. – leemes

Problemi correlati