2014-12-10 10 views
12

La mia domanda riguarda questo codice molto semplice e breve in cui viene tentata una risoluzione di sovraccarico tra due funzioni non modello che accettano un parametro di riferimento matrice. La domanda è stata pubblicata altrove, ma in un contesto di deduzione del modello. Ecco il codice:Initializer_list come argomento per un parametro di riferimento di matrice in un contesto non modello

#include <iostream> 

void foo (const int (&x) [3]) { std::cout << "3\n"; } 
void foo (const int (&x) [2]) { std::cout << "2\n"; } 

int main() 
{ 
    foo({1,2,3}); 
} 

g ++ 4.8.3 compila questo codice selezionando la prima funzione (suppongo), l'unica praticabile, mentre clang 3.4 non si compila, dicendo che la chiamata a foo è ambiguo (perché?).

Quale compilatore fa la cosa giusta?

clang non compila il codice rimuovendo anche il secondo sovraccarico: sembra che un inizializzatore non sia semplicemente accettato per inizializzare un riferimento di array.

È questo buggy?

+0

"clang non compilare il codice anche rimuovendo il secondo sovraccarico "- Lo fa per me (se il secondo sovraccarico viene rimosso come dici tu, usando clang 3.4.2, a patto che' -std = C++ 11' sia incluso nelle opzioni della riga di comando . Quale versione e quale invocazione non riesce per te? – hvd

+0

Hai ragione: dopo aver rimosso il secondo overload ho compilato il codice dimenticando il flag -std = C++ 11. Perdonami per quello. – GSi

+0

Ora ho definito un alias di comando per evitare un simile errore in futuro ... – GSi

risposta

5

ritengo che, sebbene il comportamento di GCC è più utile, è di comportamento che è corretto per clang C++ 11:

13.3.3.1.5 sequenza di elenco di inizializzazione [over.ics.list]

2 Se il tipo di parametro è std::initializer_list<X> o "vettore di X" e tutti gli elementi della lista di inizializzazione può essere implicitamente convertito X, la sequenza conversione implicita è il peggior conversione necessaria per convertire un elemento della lista di X .

Questa sequenza di conversione non presta attenzione alla lunghezza dell'array. Entrambi gli overload di funzioni forniscono una sequenza di conversione implicita che è una conversione di identità: entrambi prendono un riferimento a un array di int e ogni elemento nell'argomento di funzione è un int.

risoluzione di sovraccarico vede poi due conversioni di identità, e anche se lo standard ha alcune eccezioni per la risoluzione dei conflitti sulle conversioni di pari rango, non c'è nessuno che presta attenzione alla lunghezza dell'array:

13,3. 3.2 Classifica sequenze conversione implicita [over.ics.rank]

3 Due sequenze di conversione implicito della stessa forma sono sequenze di conversione indistinguibili meno che una delle regole seguenti condizioni:

seguito da un elenco che non menziona affatto gli array.

Jonathan Wakely sottolinea che questo è cambiato da allora. La tua domanda è esattamente ciò che ha richiesto tale modifica e il DR corrispondente è #1307. In C++ 14, il tuo codice è valido, ma non in C++ 11.

+1

Il tipo di parametro non è 'std :: initializer_list', e il documento di lavoro corrente gestisce matrici e' initializer_list' separatamente in diversi paragrafi, menzionando specificamente la lunghezza dell'array: _ "Altrimenti, se il tipo di parametro è" array di NX ", se l'elenco di inizializzazione ha esattamente N elementi o se ha meno di N elementi e X è costruttibile di default, e se tutti gli elementi del la lista di inizializzazione può essere convertita implicitamente in X, la sequenza di conversione implicita è la peggiore conversione necessaria per convertire un elemento della lista in X. "_ –

+0

@JonathanWakely Dice" 'std :: initializer_list ' o "array di' X' "e il tipo di parametro è un array. Verrà controllato il testo di articoli più recenti – hvd

+3

http://open-std.org/jtc1/sc22/wg21 /docs/cwg_defects.html#1307 –

4

DR 1232 modificato C++ 11 per consentire le funzioni di chiamata con parametri riferimento-array da un elenco di inizializzazione. (Le modifiche al foglio di lavoro sono mostrate in N3262). Tutti i compilatori testati implementano tale regola.

DR1307 quindi modificato di nuovo lo stesso testo per risolvere l'ambiguità che hai scoperto.Il tuo codice è accettato dai compilatori GCC e EDG, quindi presumo che Clang non lo applichi ancora.

È interessante notare che anche dopo che il DR è stato risolto, foo({1, 2}) è ancora ambiguo e rifiutato da tutti i compilatori. La ragione è che {1, 2} può legarsi al parametro const int(&)[2] (ovviamente) ma può anche legarsi al parametro const int(&)[3] perché int è default-costruibile, quindi lo stesso significato {1, 2, int()} cioè {1, 2, 0}

+0

"' foo ({1, 2}) 'è ancora ambiguo" - Sei sicuro? "Sequenza di inizializzazione elenco L1 non è una sequenza di conversione migliore della sequenza di inizializzazione elenco L2 se ... L1 converte in tipo" matrice di N1 T ", L2 converte in tipo" matrice di N2 T "e N1 è più piccolo di N2. " risolvilo in favore del sovraccarico 'const int (&) [2]'? – hvd