c++ templates gcc clang overload-resolution

c++ - Diferencia de comportamiento de resolución de sobrecarga entre GCC y clang(SFINAE)



templates overload-resolution (2)

C ++ 03 usa esta redacción como parte de la especificación para lo que generalmente se denomina SFINAE (14.8.2 Deducción de argumento de plantilla [temp.deduct], párrafo 2):

[...] Si una sustitución en un parámetro de plantilla o en el tipo de función de la plantilla de función da como resultado un tipo no válido, la deducción de tipo falla. [...]

Por el contrario, C ++ 11 usa esta redacción (14.8.2 Deducción de argumento de plantilla [temp.deduct], párrafo 8):

Si una sustitución produce un tipo o expresión no válida, la deducción de tipo falla. [...] Solo los tipos y expresiones no válidos en el contexto inmediato del tipo de función y sus tipos de parámetros de plantilla pueden dar como resultado un error de deducción. [...]

El énfasis es mío. Según tengo entendido, la redacción se mejoró en C ++ 11 para delinear de manera inequívoca lo que debería dar lugar a SFINAE (los llamados errores blandos) y lo que no debería (errores graves). Este documento de 2008 es un ejemplo de la discusión que se estaba llevando a cabo en ese momento y condujo a las reglas actuales.

Teniendo esto en cuenta, puede darse el caso de que de acuerdo con C ++ 03, una implementación puede ser correcta para aceptar su código (e incluso tal vez debería). Sin embargo, sospecho que una implementación de C ++ 11 debería rechazarlo: el error ( int::type ) está en el contexto de meta<int> , no de foo<int> .

GCC acepta el siguiente código:

template <typename T> struct meta { typedef typename T::type type; }; struct S {}; template <typename T> typename meta<T>::type foo(T, S); int foo(int, int); int main() { foo(0, 0); }

Pero clang lo rechaza con el siguiente error:

test.cpp:4:22: error: type ''int'' cannot be used prior to ''::'' because it has no members typedef typename T::type type; ^ test.cpp:10:10: note: in instantiation of template class ''meta<int>'' requested here typename meta<T>::type foo(T, S); ^ test.cpp:10:24: note: while substituting deduced template arguments into function template ''foo'' [with T = int] typename meta<T>::type foo(T, S); ^

Esto parece sugerir una diferencia en el orden en que GCC y Clang realizan ciertas operaciones durante la resolución de sobrecarga. GCC parece descartar la plantilla candidata debido a la falta de coincidencia de tipos en el segundo parámetro ( S vs. int ) antes de intentar crear una instancia del tipo de devolución de la plantilla candidata, mientras que clang parece hacerlo al revés.

Quien tiene razon

Creo que esta pregunta tiene implicaciones importantes para los autores de las bibliotecas de plantillas. Específicamente, si clang tiene razón, el autor de la plantilla foo tendría que hacer un trabajo extra para convertir el error en una falla de sustitución.

EDITAR : tenga en cuenta que el siguiente ejemplo, un poco más simple, es rechazado por GCC y clang, con errores similares:

template <typename T> struct meta { typedef typename T::type type; }; template <typename T> typename meta<T>::type foo(T); int foo(int); int main() { foo(0); }

sugiriendo que GCC sabe que "solo los tipos y expresiones no válidos en el contexto inmediato del tipo de función y sus tipos de parámetros de plantilla pueden dar como resultado un error de deducción". La diferencia entre este ejemplo y el original es la presencia del segundo parámetro de función en el ejemplo original, sobre la base de que GCC desecha la plantilla candidata antes de que incluso intente realizar una sustitución en el tipo de devolución. Creo que la pregunta es si GCC tiene razón al hacer las cosas en ese orden o si debería intentar realizar una sustitución en el tipo de retorno antes de considerar las coincidencias en los tipos de argumento.

ACTUALIZACIÓN : la respuesta de Luc Danton me convenció de que clang es correcto para rechazar el código. En consecuencia, he presentado un error GCC .


Tanto Clang como g ++ son correctos aquí.

Según la respuesta de Luc Danton, un compilador tiene permitido deducir T = int para la plantilla de función foo . Luego, durante la sustitución de ese valor en la declaración de foo , se requiere la creación de instancias implícita de meta<int> , y se produce un error fuera del contexto inmediato de la sustitución (por lo que SFINAE no se aplica). Así que Clang tiene razón al rechazar este código.

Sin embargo, [temp.inst] p7 dice:

Si el proceso de resolución de sobrecarga puede determinar la función correcta a la que llamar sin crear una instancia de una definición de plantilla de clase, no se especifica si esa instanciación realmente tiene lugar.

Debido a que foo sin plantilla es una coincidencia exacta para los argumentos en la llamada, un compilador podría determinar que la especialización de una plantilla de función nunca será la mejor función viable y, por lo tanto, no es necesario realizar la deducción y sustitución de argumentos. Por lo tanto g ++ es correcto no rechazar este código.