Inconsistencia de C++ entre gcc y clang


Me encontré con una inconsistencia de C++ entre gcc (versiones 4.8.1, 4.8.2) y clang (versiones 3.3, 3.4). Me pregunto cuál es la correcta. Aquí está el programa:

template < typename T > struct Result {};
template < typename T > struct Empty {};

template < typename T >
struct Bad_Type_Fcn {
    typedef typename Empty< T >::type type;
};

template < typename T >
Result< T >
f( const T& ) {
    return Result< T >();
}

template< class U >
Result< typename Bad_Type_Fcn< U >::type >
f( const U&, int ) {
    return Result< typename Bad_Type_Fcn< U >::type >();
}

int main() {
    (void)f< int >(42);
}

Claramente, este código no está destinado a hacer nada; es una simplificación agresiva de algo que aparece en la biblioteca Boost Range (con f simplificando make_iterator_range). La Bad_Type_Fcn es una función de tipo (técnicamente, una struct) que nunca debe ser instanciada, porque Empty<T>::type nunca existe, para cualquier T. El la presencia de este struct y de la segunda especialización de plantilla de f() no es un error en sí mismo. IRL, f() proporciona cierta funcionalidad para ciertos tipos para los que Bad_Type_Fcn no está vacío. Sin embargo, esa no es la preocupación aquí, por lo que las simplifiqué. Todavía quiero que f() funcione para tipos donde Bad_Type_Fcn está vacío.

Estoy compilando con {g++|clang++} [-std=c++0x] -pedantic -Wall -Wextra -c. La selección estándar de idioma no parece hacer una diferencia. Con clang, el programa compila sin errores ni advertencias. Con gcc, obtengo un error:

weird.cpp: In instantiation of ‘struct Bad_Type_Fcn<int>’:
weird.cpp:17:5:   required by substitution of ‘template<class U> Result<typename Bad_Type_Fcn<T>::type> f(const U&, int) [with U = int]’
weird.cpp:22:26:   required from here
weird.cpp:6:43: error: no type named ‘type’ in ‘struct Empty<int>’
         typedef typename Empty< T >::type type;

Lo que parece estar sucediendo es que clang elimina la segunda sobrecarga de f(), probablemente(?) sobre la base de que la llamada se realiza solo con 1 argumento, integer 42, mientras que la segunda sobrecarga requiere 2 argumentos. Por otro lado, gcc no elimina la segunda sobrecarga, y en su lugar intenta instanciar struct Bad_Type_Fcn<int>, lo que resulta en un error.

La inconsistencia desaparece si elimino la instanciación explícita en la llamada a f(), y escribe (void)f(42); en su lugar.

¿Cuál de los compiladores es correcto?

Author: Angew, 2014-03-07

1 answers

Recuerdo una discusión central del WG21 sobre esto, y uno de los desarrolladores de Clang defendió su posición citando 14.7.1p7

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

Por otro lado, para un programa mal formado (que es el caso aquí cuando se hace la instanciación requerida), no hay tal noción de "la función correcta para llamar", así que estoy de acuerdo con la posición de otro tipo en esa discusión que dijo que no puede ver que esto le permite a Clang ir por esa ruta.

En el ejemplo de p7 muestra el código que está bien formado tanto con como sin hacer la instanciación adicional.

En cualquier caso, incluso si a Clang se le permite hacerlo, la buena forma de su programa dependería de circunstancias particulares (comportamiento no especificado). Por lo tanto, el Estándar ya no requiere que su programa sea aceptado, y honestamente no se lo que eso significa. Considero que ese código está mal formado.

 16
Author: Johannes Schaub - litb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-03-07 19:52:58