2009-05-04 8 views
22

por favor considere este código:SFINAE con parámetros inválidos de tipo de función o tipo de matriz?

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

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

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

me esperaba que hacer SFINAE y eligiendo la segunda sobrecarga, ya que la sustitución de T en T[1] rendimientos

void [1]() 

¿Qué es un tipo no válido, por supuesto. El ajuste de los tipos de parámetros (matriz-> puntero) se realiza después de sustituir los parámetros de la plantilla en parámetros de funciones y se comprueba la validez de los tipos resultantes, como se describe en 14.8.2 [temp.deduct].

Pero tanto Comeau y GCC fallan para compilar el anterior. Ambos con diferentes diagnósticos.

Comeau dice:

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

CCG dice (versión 4.3.3):

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

Significado, GCC no deja de sustituir, pero elige la primera sobrecarga de f, devolviendo un sizeof de 1, en lugar de no sustituirlo por adelantado como Comeau.

¿Qué compilador tiene razón y mi código es válido? Consulte o cite la sección estándar adecuada en su respuesta. ¡Gracias!


actualización: El estándar en sí contiene un ejemplo de ello en la lista a 14.8.2/2. No sé por qué me daba primero:

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

Mientras que el ejemplo es de carácter informativo, muestra la intención de todos los párrafos misteriosos y parece mostrar el código anterior debería funcionar y rechazar la primera sobrecarga.

+18

litb pidiendo una aclaración normas es una contradicción :) – JaredPar

+1

hice la misma en Usenet, pero envió la pregunta aquí también, así que puede ser archivada en SO y la gente lo encontrará en la búsqueda de SO. Enlace de descarga directa: http://groups.google.com/group/comp.lang.c++.moderated/browse_thread/thread/78f8cd8cf27778e3 –

+13

pensé litb es el estándar ...? –

Respuesta

12

Una nota pequeña, aunque muy rara, he encontrado algunas ocasiones en las que creer que el compilador Comeau se ha equivocado - si bien, estos ocasiones son tan raras que su siempre vale doble y triple comprobar su hipótesis!

que puede tener una razón para el comportamiento de g ++. No estoy seguro de su especificado exactamente cuando se ajustan los tipos de parámetros:

considerar lo siguiente:

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

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

La definición de 'bar' es legal, como "T [10]" decae "T * ". Yo no se ve nada en la norma que prohíbe el compilador de realizar los ajustes de 8.3.5 contra la declaración de plantilla, y también mejora el rendimiento cuando se trata de sobrecargar a juego.

Aplicando esto a su ejemplo, g ++ podrían tratándolo como:

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

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

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

En lo anterior, el parámetro sustituido es un puntero legal a función, en lugar de una serie de funciones.

Entonces, la pregunta para mí es: ¿hay algo que prohíba los ajustes para los parámetros de la función (8.3.5) dos veces?

En lo personal, creo que tiene sentido para permitir los ajustes que sucedan dos veces ya que si no se complica la coincidencia de plantilla de función sobrecargas

En conclusión, creo que es válido por g ++ para seleccionar la primera sobrecarga basada de cómo se va desgastando treates parámetros de matriz, y Comeau está mal no tener un fallo de la deducción para la matriz de funciones.

Por supuesto, esto significa que (si Comeau se corrigió) entonces cada compilador elegiría una sobrecarga diferente y seguiría siendo compatible con los estándares ! :(

EDIT:

Sólo para ilustrar mi punto, considere el siguiente código:.

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); 
} 

Aquí, foo ha sido declarado y volver a declarar varias veces ¿Qué declaración, por lo que el parámetro escriba, si el compilador aplica las reglas enumeradas en 14.8.2?

Mi punto es que el estándar no dice nada sobre lo anterior. También me atrevería a decir que cualquier texto sobre esto tendría dejarlo como comportamiento "indefinido" o "definido por la implementación".

+0

hmm buen punto sobre múltiples declaraciones. No tengo idea de qué se elige allí, en realidad. Tendré que buscarlo más tarde. Gracias por tu ayuda amigo :) También creo que ahora sería una buena idea decir que primero decae (usando "T como elemento/tipo de función"), luego sustituye, luego se descompone nuevamente. –

+0

Cuanto más pienso en esto, más siento que merece tener un problema central abierto para él. Probé algunos ejemplos con Comeau y parece ser el primer tipo de función que se usa para realizar la comprobación siempre. Sin embargo, esto no es sensato, ya que implica que el orden en que se ven las declaraciones es importante, algo que no debería ser el caso. –

+0

Otra posibilidad sería declarar que dos declaraciones de plantilla que tienen idénticas listas de tipos de parámetros son funcionalmente equivalentes (definidas en 14.6.6.1/6 - hasta ahora solo para expresiones, aún no para tipos de parámetros). Pero diciendo que son equivalentes si los parameters-type-list no modificados son los mismos. Entonces, si las declaraciones de una plantilla de función no son equivalentes, sino que son equivalentes funcionales, el programa está mal formado; no se requiere diagnóstico. –

1

Sorprendentemente suficiente - esto funciona en VS2008. No creo que eso es necesariamente evidencia sobre su comportamiento correcto o no, aunque ...

Visual Studio se interpretting

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

como una función que toma una matriz de tamaño 1 de T, y devuelve una referencia a una serie de caracteres de tamaño 1.

1

voy a tratar de describir el proceso de deducción plantilla argumento como yo lo entiendo de la lectura de la norma.

  1. Los argumentos explícitos de la plantilla se comprueban como se describe en 14.8.2/2.
  2. La firma de la función resultante se ajusta según 8.3.5 (es decir, se lleva a cabo la desintegración de matriz a puntero).
  3. Los argumentos de plantilla implícitos se deducen según 14.8.2.1 (esto se realiza en una firma parcialmente sustituida del paso 2).

La deducción de la primera sobrecarga falla en el paso 1, por lo que la resolución de sobrecarga devuelve la segunda sobrecarga. No creo que el programa esté mal formado.

+1

, pero una falla en la deducción no debe dar como resultado una falla en la compilación. 14.8.3/1 dice "Si, para una plantilla de función dada, falla la deducción de argumentos, no se agrega dicha función al conjunto de funciones candidatas para esa plantilla". En ninguna parte se afirma que tal falla de deducción da como resultado que un programa esté mal formado. –

+0

litb, tienes razón, por supuesto. – avakar

+0

Sin embargo, es poco perturbador que Comeau esté equivocado. – avakar

Cuestiones relacionadas