2010-11-12 13 views
7

Al crear un pequeño programa de ejemplo con Microsoft VisualStudio 2008 noté algo extraño sobre la deducción de tipos pasados ​​a las plantillas. Considere este ejemplo:¿Por qué se eliminan los calificadores de los argumentos de la plantilla al deducir el tipo?

template<class T> 
void f(T v) { 
    x; // trigger a compile error 
    (void)v; 
} 

template<class T> 
void g(T v) { 
    f(v); 
} 

void h() { 
    int i; 
    g<const int &>(i); 
} 

Compilar este ejemplo utilizando cl /c foo.cpp produce un error de compilación (como se pretende). Lo que es interesante es el valor del parámetro de la plantilla 'T'. Esto es lo que VisualStudio 2008 impresiones:

mini.cpp(3) : error C2065: 'x' : undeclared identifier 
     mini.cpp(9) : see reference to function template instantiation 'void f<int>(T)' being compiled 
     with 
     [ 
      T=int 
     ] 
     mini.cpp(14) : see reference to function template instantiation 'void g<const int&>(T)' being compiled 
     with 
     [ 
      T=const int & 
     ] 

Nótese cómo en g, el tipo del argumento es const int & pero en f es sólo int. Aparentemente, la parte de referencia a resistencia se eliminó al deducir el tipo que se usaría al crear la plantilla f. Al ajustar el ejemplo de modo que f se invoca como

f<T>(v); 

el tipo es const int & tanto en f y g. ¿Porqué es eso? ¿Es este comportamiento específico? Confié en secreto en el tipo de argumento de la función v que se pasará al f, pero aparentemente no es así.

+0

'C++' Plantillas + MSVC++ = Mala combinación. –

+2

@Prasoon: GCC deduce el mismo tipo.Por supuesto, en GCC, el código del que pregunta desencadena un error de compilación en 'f' de la plantilla, antes de que se acerque a pensar en instanciarlo, porque GCC hace la compilación en dos fases correctamente. 'x' no depende de un parámetro de plantilla, por lo que debe rechazarse en la primera fase (como en GCC), no en la segunda (como en MSVC). Pero cambie 'x' a' v = 1; ', y se ve fácilmente que GCC no crea instancias de' f' con 'const int &' a menos que especifique 'f (v)' en 'g' explícitamente. –

Respuesta

6

La respuesta es que, aunque la variable v tiene tipo const int &, la expresión v es una expresión lvalue con el tipo const int.

litb proporciona el texto (5/6): "Si una expresión tiene inicialmente el tipo" referencia a T "(8.3.2, 8.5.3), el tipo se ajusta a" T "antes de cualquier análisis posterior , la expresión designa el objeto o función denotada por la referencia, y la expresión es un valor l ".

Un "argumento" es "una expresión en la lista separada por comas delimitada por los paréntesis en una expresión de llamada de función" (1.3.1). Por lo tanto, en 14.8.2.1:

  • "el tipo de argumento correspondiente de la llamada (llámalo A)" es const int.
  • "Si A es un tipo cv-calificado, los cv-calificadores de nivel superior del tipo A se ignoran para la deducción de tipo" (de ahí, int).
  • "el proceso de deducción de los intentos para encontrar valores de los argumentos de plantilla que harán que el deducido Un idéntica a A" (por lo que T es int)
+0

probablemente tienes razón. La terminología estándar con A y P me confundió por completo = | – icecrime

+0

@icecrime: yo también, no es parte del estándar con el que estoy tan familiarizado. Creo que los ajustes a "P" significan que si 'f' se declaró' plantilla void f (const T v) ', o' plantilla void f (T & v) ', entonces T se usaría para deducción de tipo. Aunque no estoy seguro de entenderlo correctamente. –

+0

Incluso si esto no fuera 100% correcto, está lo suficientemente cerca. "Obviamente" si miramos la expresión 'v + v', todo el mundo esperaría que se comportara como una suma entera. Ahí no tiene sentido tratar 'int &' de forma diferente a 'int'. – MSalters

1

http://accu.org/index.php/journals/409 es un artículo bastante extenso, pero que explica el proceso. A partir de los parámetros de plantilla, se deriva un tipo de parámetro P, y esto se corresponde con un tipo de argumento A. La parte relevante es donde se describe cómo se deriva un tipo de destino A del argumento de la función: para los tipos que no son de matriz, las referencias simplemente se eliminan. Por lo tanto, si el tipo de argumento es int&, entonces el tipo de destino A es simplemente int.

Esta es la razón simple: porque la norma nos lo dice. ¿Cuál es el razonamiento? Como sucede, el artículo tiene una nota al pie que señala eso también. En su ejemplo, typeid(v)==typeid(const int). Cuando se usan en contextos que no son de valor l, los tipos de referencia se comportan como tipos que no son de referencia.

Cuestiones relacionadas