2009-05-04 5 views
22

perche questo codice:SFINAE con parametri di tipo funzione o array non validi?

template<typename T> 
char (&f(T[1]))[1]; 

template<typename T> 
char (&f(...))[2]; 

int main() { char c[sizeof(f<void()>(0)) == 2]; } 

mi aspettavo di fare SFINAE e scegliendo la seconda sovraccarico, dal momento che la sostituzione di T in T[1] rendimenti

void [1]() 

che è un tipo non valido, naturalmente. La regolazione dei tipi di parametri (array-> puntatore) viene eseguita dopo la sostituzione dei parametri del modello in parametri di funzione e il controllo di tipi risultanti validi come 14.8.2 [temp.deduct] descrive.

Ma sia Comeau che GCC non riescono a compilare quanto sopra. Entrambi con diagnostica diversa.

Comeau dice:

"ComeauTest.c", line 2: error: array of functions is not allowed char (&f(T[1]))[1];

GCC dice (versione 4.3.3):

error: ISO C++ forbids zero-size array c

Significato, GCC non manca di sostituirlo, ma sceglie il primo overload di f, restituendo una sizeof di 1, invece di non sostituirlo davanti come Comeau.

Quale compilatore ha ragione ed il mio codice è valido? Si prega di fare riferimento o di citare la sezione standard appropriata nella risposta. Grazie!


Aggiornamento: la norma in sé contiene un esempio nella lista a 14.8.2/2. Non so perché ho trascurato per primo:

template <class T> int f(T[5]); 
int I = f<int>(0); 
int j = f<void>(0); // invalid array 

Mentre l'esempio è solo informativo, mostra l'intenzione di tutti quei paragrafi misteriosi e sembra mostrare il codice di cui sopra dovrebbe funzionare e respingere la prima sovraccarico.

+18

litb chiedendo un chiarimento degli standard è una contraddizione :) – JaredPar

+1

ho chiesto lo stesso su usenet, ma ho postato la domanda anche qui, in modo che possa essere archiviata su SO e la gente lo troverà durante la ricerca su SO. Link a usenet: http://groups.google.com/group/comp.lang.c++.moderated/browse_thread/thread/78f8cd8cf27778e3 –

+13

Pensavo che litb fosse lo standard ...? –

risposta

12

Una piccola nota, anche se molto raro, ho trovato alcune occasioni in cui ho credere che il compilatore Comeau ha sbagliato - anche se, questi occasioni sono così rare che la sua sempre la pena doppie e triple verifica l'ipotesi!

Potrei avere una ragione per il comportamento di g ++. Non sono sicuro che la sua specificato esattamente quando tipi di parametri vengono regolati:

Si consideri il seguente:

template<typename T> 
struct A 
{ 
    void bar (T[10]); 
}; 

template<typename T> 
void A<T>::bar (T*) 
{ 
} 

La definizione di 'bar' è legale, come "T [10]" decade a "T *". Io faccio non vedo nulla nello standard che proibisca il compilatore da eseguendo gli aggiustamenti di 8.3.5 contro la dichiarazione modello, e migliora anche le prestazioni quando si tratta di sovraccaricare la corrispondenza.

Applicando questo al vostro esempio, g ++ potrebbe essere trattandolo come:

template<typename T> 
char (&f(T*))[1]; 

template<typename T> 
char (&f(...))[2]; 

int main() { char c[sizeof(f<void()>(0)) == 2]; } 

In quanto sopra, il parametro sostituito è un puntatore legale funzione, piuttosto che una serie di funzioni.

Quindi, la domanda per me è - è se c'è qualcosa che impedisce le regolazioni per i parametri di funzione (8.3.5) due volte?

Personalmente, penso che ha senso per consentire gli adeguamenti accadano due volte da quando altrimenti si complica l'abbinamento del modello di funzione sovraccarichi

In conclusione, credo che il suo valido per g ++ per selezionare il primo overload base su come tratta i parametri dell'array decadente, e Comeau ha torto di non avere un errore di deduzione per l'array di funzioni.

Naturalmente questo significa che ora (se Comeau è stato fissato) allora ogni compilatore avrebbe scelto un sovraccarico di diverso e sarebbe ancora norme compatibile! :(

EDIT:

Giusto per illustrare il mio punto, si consideri il seguente codice:.

template <typename T> void foo (T *); 
template <typename T> void foo (T * const); 
template <typename T> void foo (T []); 
template <typename T> void foo (T [10]); 
template <typename T> void foo (T [100]); 

void bar() 
{ 
    foo < void() > (0); 
} 

Qui, foo è stato dichiarato e ridichiarato più volte che la dichiarazione, e quindi quale parametro tipo, nel caso in cui il compilatore applichi le regole elencate in 14.8.2?

Il mio punto è che lo standard non dice nulla di quanto sopra.Vorrei anche arrivare a dire che qualsiasi formulazione su questo avrebbe lasciarlo come comportamento "non definito" o "implementazione definita".

+0

hmm buon punto su più dichiarazioni. Non ho idea di cosa viene scelto lì, in realtà. Devo cercarlo più tardi. Grazie per il tuo amico amico. Penso anche che sarebbe una buona idea limitarsi a decadere prima (usando "T come elemento/tipo di funzione"), poi sostituisce, quindi decompone di nuovo. –

+0

Più penso a questo, più sento che merita di avere un problema di base aperto per questo. Ho provato alcuni esempi con Comeau e sembra essere il primo tipo di funzione che viene utilizzato per eseguire il controllo sempre. Tuttavia, questo non è ragionevole, poiché implica che l'ordine in cui le dichiarazioni sono viste è importante - qualcosa che non dovrebbe essere il caso. –

+0

Un'altra possibilità sarebbe quella di affermare che due dichiarazioni di template che hanno liste di parametri identici sono equivalenti dal punto di vista funzio- nale (definite in 14.6.6.1/6 - finora solo per le espressioni, non ancora per i tipi di parametro). Dicendo che sono equivalenti se la lista dei parametri non modificati è la stessa. Quindi, se le dichiarazioni di un modello di funzione non sono equivalenti, ma equivalenti funzionali, il programma è mal formato; nessuna diagnostica richiesta. –

1

Sorprendentemente, questo funziona in VS2008. Non credo che sia necessariamente la prova al fatto che è un comportamento corretto o meno però ...

Visual Studio è interpretting

char (&f(T[1]))[1]; 

come una funzione che prende un array di dimensione 1 di T, e restituisce un riferimento a una matrice di caratteri di dimensione 1.

1

Proverò a descrivere il processo di deduzione argomento modello come ho capito dalla lettura dello standard.

  1. Gli argomenti del modello esplicito vengono controllati come descritto in 14.8.2/2.
  2. La firma della funzione risultante viene regolata come in 8.3.5 (cioè, l'allineamento al decadimento del puntatore viene eseguito).
  3. Argomenti di modello impliciti dedotti secondo 14.8.2.1 (questo viene eseguito su una firma parzialmente sostituita dal passaggio 2).

La detrazione per la prima sovraccarico fallisce al punto 1, la risoluzione di sovraccarico restituisce quindi il secondo sovraccarico. Non credo che il programma sia mal formato.

+1

un errore di deduzione non dovrebbe tuttavia comportare un errore di compilazione. 14.8.3/1 dice "Se, per un dato modello di funzione, la deduzione dell'argomento non riesce, nessuna funzione di questo tipo viene aggiunta all'insieme di funzioni candidate per quel modello.". Da nessuna parte si afferma che un tale fallimento di deduzione si traduce in un programma mal formato. –

+0

litb, hai ragione, ovviamente. – avakar

+0

Tuttavia, è un po 'inquietante che Comeau abbia torto. – avakar