Domanda

Per qualche motivo ho pensato che C ++ 0x sia consentito std::initializer_list come argomento funzione per funzioni che si aspettano tipi che possono essere costruiti da tale, ad esempio std::vector. Ma a quanto pare, non funziona. Questo è solo il mio compilatore o non funzionerà mai? È a causa dei potenziali problemi di risoluzione del sovraccarico?

#include <string>
#include <vector>

void function(std::vector<std::string> vec)
{
}

int main()
{
    // ok
    std::vector<std::string> vec {"hello", "world", "test"};

    // error: could not convert '{"hello", "world", "test"}' to 'std::vector...'
    function( {"hello", "world", "test"} );
}
È stato utile?

Soluzione

GCC ha un bug. Lo standard lo rende valido. Vedere:

Si noti che ci sono due lati di questo

  • Come e cosa viene fatto l'inizializzazione in generale?
  • Come viene utilizzata l'inizializzazione durante la risoluzione del sovraccarico e quale costo ha?

La prima domanda ha una risposta nella sezione 8.5. La seconda domanda ha una risposta nella sezione 13.3. Ad esempio, il legame di riferimento viene gestito a 8.5.3 e 13.3.3.1.4, mentre l'inizializzazione dell'elenco viene gestita 8.5.4 e 13.3.3.1.5.

8.5/14,16:

L'inizializzazione che si verifica nella forma

T x = a;

così come nella discussione di passaggio, il ritorno della funzione, il lancio di un'eccezione (15.1), la gestione di un'eccezione (15.3) e l'inizializzazione dei membri aggregati (8.5.1) è chiamata Inizializzazione della copia.
.
.
La semantica degli inizializzatori è la seguente [...]: se l'inizializzatore è un elenco di init rinforzato, l'oggetto è elenco iniziale (8.5.4).

Quando si considera il candidato function, il compilatore vedrà un elenco di inizializzatori (che non ha ancora tipo - è solo un costrutto grammaticale!) Come argomento e un std::vector<std::string> come parametro di function. Per capire qual è il costo della conversione e se noi Potere Convertili nel contesto del sovraccarico, 13.3.3.1/5 dice

13.3.3.1.5/1:

Quando un argomento è un elenco di inizializzatori (8.5.4), non è un'espressione e si applicano regole speciali per convertirlo in un tipo di parametro.

13.3.3.1.5/3:

Altrimenti, se il parametro è una risoluzione di classe X non aggregata e sovraccarico per 13.3.1.7 sceglie un singolo miglior costruttore di X per eseguire l'inizializzazione di un oggetto di tipo X dall'elenco di inizializzatore argomento, la sequenza di conversione implicita è un utente- sequenza di conversione definita. Le conversioni definite dall'utente sono consentite per la conversione degli elementi dell'elenco inizializzatore nei tipi di parametri del costruttore, ad eccezione di quanto indicato in 13.3.3.1.

La classe non aggregata X è std::vector<std::string>, e troverò il miglior costruttore singolo di seguito. L'ultima regola ci concede di utilizzare conversioni definite dall'utente in casi come le seguenti:

struct A { A(std::string); A(A const&); };
void f(A);
int main() { f({"hello"}); }

Siamo autorizzati a convertire la corda letterale a std::string, anche se ciò richiede una conversione definita dall'utente. Tuttavia, indica le restrizioni di un altro paragrafo. Cosa fa 13.3.3.1 dire?

13.3.3.1/4, che è il paragrafo responsabile della proibizione di più conversioni definite dall'utente. Esamineremo solo le inizializzazioni dell'elenco:

Tuttavia, quando si considera l'argomento di una funzione di conversione definita dall'utente [(o costruttore)] che è un candidato di [...] 13.3.1.7 quando si passa l'elenco inizializzatore come un singolo argomento o quando l'elenco iniziale ha esattamente un elemento e una conversione in una classe X o riferimento a (possibilmente CV-qualificazione) X è considerata per il primo parametro di un costruttore di X o [...], sono consentite solo sequenze di conversione standard e sequenze di conversione Ellipsis.

Si noti che si tratta di una restrizione importante: se non fosse per questo, quanto sopra può utilizzare il costruttore di copia per stabilire una sequenza di conversione altrettanto bene e l'inizializzazione sarebbe ambigua. (Nota la potenziale confusione di "A o B e C" in quella regola: ha lo scopo di dire "(A o B) e C" - Quindi siamo limitati solo Quando si tenta di convertire da un costruttore di x con un parametro di tipo X).

Siamo delegati a 13.3.1.7 Per raccogliere i costruttori che possiamo usare per fare questa conversione. Ci avviciniamo a questo paragrafo dal lato generale a partire da 8.5 a cui ci ha delegato 8.5.4:

8.5.4/1:

L'inizializzazione dell'elenco può verificarsi in contesti di inizializzazione diretta o di inizio della copia; viene chiamato l'elenco inizialezzazione in un contesto di inizializzazione diretta Inizializzazione della lista diretta e si chiama l'inizializzazione dell'elenco in un contesto di inizio della copia Inizializzazione della copia-elenco.

8.5.4/2:

Un costruttore è un costruttore di elenco iniziale Se il suo primo parametro è di tipo std::initializer_list<E> o riferimento a eventualmente CV-qualificato std::initializer_list<E> Per qualche tipo E, e non ci sono altri parametri o tutti gli altri parametri hanno argomenti predefiniti (8.3.6).

8.5.4/3:

L'inizializzazione dell'elenco di un oggetto o un riferimento di tipo T è definito come segue: [...] altrimenti, se T è un tipo di classe, vengono considerati i costruttori. Se T ha un costruttore di elenco iniziale, l'elenco degli argomenti consiste nell'elenco inizializzatore come singolo argomento; Altrimenti, l'elenco degli argomenti consiste negli elementi dell'elenco inizializzatore. I costruttori applicabili vengono elencati (13.3.1.7) e il migliore viene scelto attraverso la risoluzione del sovraccarico (13.3).

A quest'ora, T è il tipo di classe std::vector<std::string>. Abbiamo un argomento (che non ha ancora un tipo! Siamo solo nel contesto di avere un elenco di inizializzatori grammaticali). I costruttori sono elencati da 13.3.1.7:

...] Se T ha un costruttore di elenco iniziale (8.5.4), l'elenco degli argomenti è costituito dall'elenco inizializzatore come singolo argomento; Altrimenti, l'elenco degli argomenti consiste negli elementi dell'elenco inizializzatore. Per l'inizializzazione della copia-list, le funzioni candidate sono tutti i costruttori di T. Tuttavia, se viene scelto un costruttore esplicito, l'inizializzazione è in modo mal formato.

Considereremo solo l'elenco inizializzatore di std::vector Come unico candidato, dal momento che sappiamo già che gli altri non vincono contro di esso o non si adatteranno all'argomento. Ha la seguente firma:

vector(initializer_list<std::string>, const Allocator& = Allocator());

Ora, le regole di conversione di un elenco di inizializzatori in un std::initializer_list<T> (per classificare il costo della conversione dell'argomento/parametro) sono elencati in 13.3.3.1.5:

Quando un argomento è un elenco di inizializzatori (8.5.4), non è un'espressione e si applicano regole speciali per convertirlo in un tipo di parametro. [...] se il tipo di parametro è std::initializer_list<X> E tutti gli elementi dell'elenco inizializzatore possono essere implicitamente convertiti in X, la sequenza di conversione implicita è la peggiore conversione necessaria per convertire un elemento dell'elenco in X. Questa conversione può essere una conversione definita dall'utente anche nel contesto di una chiamata a un costruttore di prima lista.

Ora, l'elenco iniziale verrà convertito correttamente e la sequenza di conversione è una conversione definita dall'utente (da char const[N] a std::string). Come è fatto questo è dettagliato 8.5.4 ancora:

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 in base alle regole per l'inizializzazione di un oggetto da una classe dello stesso tipo (8.5). (...)

Vedere 8.5.4/4 Come viene fatto questo passaggio finale :)

Altri suggerimenti

Sembra funzionare in questo modo:

function( {std::string("hello"), std::string("world"), std::string("test")} );

Forse è un bug del compilatore, ma forse stai chiedendo troppe conversioni implicite.

Non ne sono sicuro, ma sospetto che ciò che sta accadendo qui sia che convertire in una conversione iniziale è una conversione e convertirlo in vettoriale è un'altra conversione. In tal caso, stai superando il limite di una sola conversione implicita ...

Questo è un bug del compilatore o il compilatore non supporta std :: inizializer_list. Testato su GCC 4.5.1 e compila bene.

Devi specificare il tipo di inizializer_list

function(std::initializer_list<std::string>{"hello", "world", "test"} );

Buona fortuna

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top