12

¿Cómo se debe comportar este código? Llama a la función genérica ignorando mi sobrecarga si uso nombre calificado en la función call_read(); y llama a la sobrecarga primero y luego a la versión genérica si uso nombre no calificado. ¿Cual es la diferencia? ¿Es un error en GCC?Comportamiento diferente para la búsqueda de nombre calificado y no calificado para la plantilla

#include <iostream> 

struct info1 {}; 
struct info2 {}; 

template<class T> void read(T& x) 
{ 
    std::cout << "generic" << std::endl; 
} 

template<class T> void call_read(T& x) 
{ 
    ::read(x); // if I replace ::read(x) with read(x) the overload is called 
} 

void read(info1& x) 
{ 
    std::cout << "overload" << std::endl; 
} 

int main() 
{ 
    info1 x; 
    info2 y; 
    call_read(x); 
    call_read(y); 
} 

También me di cuenta de que funciona diferente para los tipos fundamentales. Ver el código abajo

#include <iostream> 

typedef struct info1 {}; 
typedef struct info2 {}; 
typedef int info3; 
typedef double info4; 

template<class T> void read(T x) 
{ 
    std::cout << "generic" << std::endl; 
} 

template<class T> void call_read(T x) 
{ 
    read(x); 
} 

void read(info1 x) 
{ 
    std::cout << "overload" << std::endl; 
} 
void read(info3 x) 
{ 
    std::cout << "overload" << std::endl; 
} 

int main() 
{ 
    call_read(info1()); 
    call_read(info2()); 
    call_read(info3()); 
    call_read(info4()); 
} 

Se supone que llamar a la función sobrecargada dos veces, pero no lo es. Ver el resultado aquí http://codepad.org/iFOOFD52

+0

¡Parece extraño! Otra pregunta: ¿cómo se llama a 'leer (información y)', si no está visible para 'call_read (T &)'? – iammilind

+0

¿Por qué lo llamas usando ::? – Griwes

+1

No lo soy, solo para fines de prueba. Veo que se comporta de manera diferente en función de cómo se llama. Eso es interesante. – axe

Respuesta

9

Lo que estamos observando es una superposición de búsqueda de nombre de dos fases y argumento de búsqueda depende.

Veamos lo que dice la norma (C++ 03). [Temp.dep]:

[...] En una expresión de la forma:

postfix-expression (expression-listopt) 

donde el postfix-expresión es un identificador, el identificador indica un nombre dependiente si y sólo si cualquiera de las expresiones en la lista de expresiones es una expresión dependiente del tipo (14.6.2.2).

Esto significa que en tanto read y ::read, read es un nombre dependiente porque x es dependiente del tipo. Eso significa que se resuelve en el momento de la creación de instancias. Vamos a ver cuáles son las reglas para este [temp.dep.candidate]:

Para una llamada a una función que depende de un parámetro de plantilla, si el nombre de la función es un rotundo-id, pero no una plantilla-id, el las funciones candidatas se encuentran utilizando las reglas de búsqueda habituales (3.4.1, 3.4.2) excepto que:

- Para la parte de la búsqueda que utiliza la búsqueda de nombres no calificados (3.4.1), solo declaraciones de funciones con enlaces externos desde el plantilla definición contexto se encuentran.

Por lo tanto, para el caso ::read solo se consideran las funciones declaradas antes de la definición de la plantilla. Pero:

- Por la parte de las operaciones de búsqueda utilizando espacios de nombres asociados (3.4.2), sólo declaraciones de funciones con enlace externo encuentran ya sea en el contexto de definición de plantilla o el contexto de instancias de plantilla se encuentran .

para las read no calificadas se consideran ambas funciones, aquellas visibles en la definición de plantilla y la creación de instancias de plantilla.

+2

No creo que esto sea cierto. x es siempre un nombre dependiente que retrasa la búsqueda a la 2da fase. La diferencia es una sintaxis que desactiva ADL y la otra no. ¿No estás de acuerdo? – sellibitze

+0

@sellibitze: Estoy de acuerdo. Pero ADL no está relacionado con la pregunta. Estoy bastante seguro de que es un error de compilación. – ybungalobill

+0

No estoy de acuerdo con su análisis. No es un error del compilador. Este es el comportamiento previsto. – sellibitze

-1

El compilador siempre llamará al método que coincida más con su llamada. Aquí llama:

read(T& x) [with T = info1] 

Por lo tanto, el compilador preferirá la sobrecarga ya que coincide exactamente con la llamada. Es en la lógica de las especializaciones de plantillas, lo que permite decir que si existe una función sobrecargada que se adapte mejor a su llamada, entonces esta será utilizada.

Para la segunda parte de la pregunta, respecto a la diferencia al usar nombres totalmente calificados y no calificados, proviene del hecho de que el nombre completamente calificado no depende de nada más y se resuelve en la primera coincidencia (aquí su declaración de plantilla).

+0

Lo que estoy diciendo es que también se debe llamar a la sobrecarga para info3, pero no es así. La única diferencia entre estas 2 sobrecargas es el tipo. Uno es fundamental y el otro no. – axe

+0

Bien, actualizaste tu código desde que escribí :) Lo leí nuevamente – Geoffroy

5

Sí, este es el comportamiento esperado. En el primer caso (:: lectura) deshabilita ADL (búsqueda dependiente del argumento) que restringe la búsqueda de nombres a cosas que se han declarado en el ámbito global antes de su uso de lectura. Si elimina :: ADL se activará, lo que puede resolver las funciones que declaró después de su plantilla de función.

Editar: Y dado que para los tipos fundamentales como int y double no hay ADL, esto explica su segunda observación.

+0

+1, para tu análisis (si es verdad). Pero, por otro lado, también puede explicar por qué 'read (into1 &)' se vuelve visible para 'call_read()'? Debido a la búsqueda de argumento de 2 fases? – iammilind

+0

@iammilind: info1 es un tipo de clase. Por lo tanto, tiene un espacio de nombre asociado y, por lo tanto, se activa ADL. Como dije, ADL no tiene restricciones para encontrar entidades que hayan sido declaradas antes de su uso. – sellibitze

+0

¡Gracias! La búsqueda de Koenig explica la diferencia entre int y estructura. – axe

Cuestiones relacionadas