2012-01-21 19 views
6
#include <iostream> 
template <class T> 
void foo(T) { 
    std::cout << "foo(T)" << std::endl; 
} 

template <class T> 
void foo(T*) { //#3 
    std::cout << "foo(T*)" << std::endl; 
} 

#define TEST 

#ifdef TEST 
template <> 
void foo(int*) { //#1 
    std::cout << "foo(int*)" << std::endl; 
} 
#else 
template <> 
void foo<int*>(int*) { //#2 
    std::cout << "foo<int*>(int*)" << std::endl; 
} 
#endif 

int main(int argc, char **argv) { 
    int* p = 0; 
    foo(p); 
    return 0; 
} 

cuál es la diferencia entre # 1 y # 2. Si defino TEST, trabajo # 1. Pero si lo comento, trabajo # 3 ... Y cuál es la forma correcta de escribir la especialización de plantilla de función ...¿Error en la especialización de la plantilla de función?

Respuesta

3

# 1 declara una plantilla de especialización en función del nº 3 y automáticamente deduce los parámetros de plantilla. # 2 es una especialización de la primera plantilla que definió (la que no tiene número, llamémosla # 0) para T=int*. No puede ser una especialización de # 3 porque reemplazar T con el int* especificado llevaría a un parámetro int**.

Cuando llame al foo, la resolución de sobrecarga ahora primero selecciona la plantilla base que mejor se ajusta, luego verifica esa plantilla para ver si hay alguna especialización existente. Con TEST definido, hay dos plantillas base (# 0 y # 3) y # 3 es una mejor coincidencia y se selecciona. Luego, el compilador busca especializaciones de esa plantilla, y # 1 es una mejor opción y se llama.

Sin definido, todavía hay dos plantillas base (# 0 y # 3) y # 3 es una mejor coincidencia y se selecciona.Luego el compilador busca especializaciones de esa plantilla, pero como # 2 se especializa en # 0 y no # 3, no se considera y el # 3 termina de ser llamado.

Este es el ejemplo clásico de Why not Specialize Function Templates. Los problemas se explican con más detalle allí.

La solución más sencilla es no especializar plantillas de función en absoluto, sino simplemente añadir nuevas sobrecargas para los tipos especiales:

// no template, just a normal function 
void foo(int*) { 
    std::cout << "foo(int*)" << std::endl; 
} 
0

Realmente no puedo decir qué función se supone que se especializa # 2, o exactamente cómo reglas de resolución de sobrecarga extremadamente complicadas seleccionarían la función para llamar.

Sé que generalmente no necesita necesita para especializarse en funciones, pero puede confiar en la sobrecarga en su lugar. Para obtener una función para int* sólo tiene

void foo(int*) { 
    std::cout << "foo(int*)" << std::endl; 
} 

Una función no molde se prefiere sobre plantillas, siempre y cuando el parámetro coincide.

+0

Hay algunos contextos en los que es conveniente o necesario especificar el parámetro de plantilla (s) . El lugar obvio donde es necesario especificar los argumentos es si el argumento de la plantilla no se deduce. Los lugares donde es conveniente es cuando necesita dirigir el argumento a un tipo adecuadamente convertido. Cuando quiera especificar explícitamente el argumento, a menudo no puede usar la sobrecarga. –

2

Para las especializaciones de plantillas de funciones, puede enumerar explícitamente los argumentos de la plantilla, pero no es necesario si los argumentos de la plantilla se deducen. Si no especifica los argumentos de la plantilla, el compilador los deduce usando las mismas reglas que la resolución de sobrecarga. Para decidir qué función de sobrecarga se debe elegir, el compilador comienza con solo mirar las plantillas primarias (que se seleccionan mediante algún proceso mágico, en primer lugar). En cuanto a las dos plantillas primarias disponibles

template <typename T> void foo(T); 
template <typename T> void foo(T*); 

este último es una mejor coincidencia para un argumento de puntero. Una vez que se encuentra la plantilla primaria adecuada, el compilador busca posibles especializaciones de esta plantilla primaria. Sin embargo, su ejemplo n. ° 2 en realidad no es una especialización de la plantilla de función tomando un argumento de puntero aunque implica un argumento de puntero. Si se toma la declaración primaria

template <typename T> void foo(T*); 

y se reemplaza por el argumento T plantilla especificada explícitamente int* se obtiene

template <> void foo<int*>(int**); 

Es decir, la declaración

template <> void foo<int*>(int*); 

es algo diferente. Es probable que sólo quiere perder el puntero cuando se especifica el parámetro de plantilla:

template <> void foo<int>(int*); 
+1

¿Por qué dices que '# 2' no debería compilarse? Se ajusta a la primera plantilla, 'plantilla void foo (T)' con 'T = int *'. –

+0

@AaronMcDaid Oh, lo siento, tienes razón: no vi que haya dos plantillas principales y la pregunta es cuál de estas se usa. Sin embargo, dado que la versión de la versión de puntero de la coincidencia primaria es una mejor opción, esta plantilla se elegiría incluso si la especialización de la misma es una mejor coincidencia: la resolución de sobrecarga funciona en términos de las plantillas primarias. Una vez que se decide la plantilla de función, se selecciona la especialización adecuada. Supongo que debería editar la respuesta para reflejar esto ... –

Cuestiones relacionadas