2010-07-20 22 views
10

He encontrado un verdadero quemahojas en C++, nunca me había sucedido antes.La función de plantilla de C++ obtiene valores predeterminados erróneos

La esencia del problema es que al invocar mi función (plantilla), los argumentos que he definido por defecto tienen sus valores codificados. Solo ocurre si llamo a la función con los valores predeterminados.

Mi función de plantilla se declara así:

template <typename T> 
vector2<T> transform(vector2<T> const &vec, matrix4<T> const &m, T z = T(0), T w = T(1)); 

Está más tarde, en la misma cabecera, define así:

template <typename T> 
inline vector2<T> transform(vector2<T> const &vec, matrix4<T> const &m, T z, T w) 
{ 
vector4<T> res = m * vector4<T>(vec.x, vec.y, z, w); 
return vector2<T>(res.x, res.y); 
} 

Ahora cuando llamo a esto con los valores predeterminados (transform(vector2<double>(0, 1), view_transform)) I no obtengo los valores que espero Entrando en transform con el depurador de VC++ veo z y w teniendo valores "divertidos" (que en mi experiencia significa que algo no se inicializa correctamente). Los valores divertidas

ejemplo sería: 0.0078125000000000000 y 2.104431116947e-317 # DEN

Ahora he intentado encontrar la respuesta en C++ FAQ Lite, googling; incluso traté de calmarme con Schubert, pero no puedo por la vida de mí resolverlo. Supongo que es muy simple y sospecho que es una especie de tontería de plantilla en el trabajo.

¿Hay alguna manera de obtener los valores predeterminados que espero y deseo, y por qué me hace esto?

Edición 1:

Si la llamada cambio por lo que utiliza flotadores lugar (transform(vector2<float>(0, 1), view_transform)) el problema desaparece. Parece que esto solo ocurre si T = double.

Edición 2:

Esto sólo ocurre si tengo dos especializaciones double y float. Si utilizo una especialización de flotación en un lugar, la doble especialización obtiene valores predeterminados extraños. Si cambio todos los lugares, se llama a la función para que use el doble de "desaparece". Todavía no entiendo por qué, sin embargo, es como si utilizara compensaciones defectuosas o algo al configurar z y w.

Datos 3:

Tales from the C++ Cripta:

#include <sgt/matrix4.hpp> 

int main(int argc, char *argv[]) 
{ 
    sgt::matrix4<double> m0(
     2, 0, 0, 1, 
     0, 2, 0, 1, 
     0, 0, 1, 0, 
     0, 0, 0, 1); 

    m0 *= m0; 

    sgt::vector2<double> blah0 = sgt::transform(sgt::vector2<double>(1, 0), m0); 

    sgt::matrix4<float> m1(
     2, 0, 0, 1, 
     0, 2, 0, 1, 
     0, 0, 1, 0, 
     0, 0, 0, 1); 

    m1 *= m1; 

    sgt::vector2<float> blah1 = sgt::transform(sgt::vector2<float>(1, 0), m1); 

    printf("%f", blah0.x); 
    printf("%f", blah1.x); 
} 

En matrix4.hpp:

// ... 

template <typename T> 
vector2<T> transform(vector2<T> const &vec, matrix4<T> const &m, T z = T(0), T w = T(1)); 

template <typename T> 
inline vector2<T> transform(vector2<T> const &vec, matrix4<T> const &m, T z, T w) 
{ 
    vector4<T> res = m * vector4<T>(vec.x, vec.y, z, w); 
    return vector2<T>(res.x, res.y); 
} 

// ... 

Si me quedo eso, la doble especialización tiene su defecto argumentos correctos, pero la versión flotante obtiene ambos argumentos por defecto como cero (0.000000) que aunque mejor, todavía no es z = 0 y w = 1.

Editar 4:

hizo un Connect issue.

+0

No veo nada malo con eso ... ¿Podría probarlo con un compilador diferente? –

+1

¿Qué compilador estás usando y en qué plataforma estás? – Alerty

+1

Ah, perdón, olvidé esa información, Microsoft VC++ 10 (16.00.30319.01). @j_random_hacker: Hmm, bueno, podría descargar MinGW e intentarlo. – Skurmedel

Respuesta

5

El siguiente falla para mi en Dev Studio:

#include "stdafx.h" 
#include <vector> 
#include <iostream> 

template <typename T> 
std::vector<std::vector<T> > transform(std::vector<std::vector<T> > const &vec, 
             std::vector<std::vector<std::vector<std::vector<T> > > > const &m, 
             T z = T(0), T w = T(1)); 


template <typename T> 
std::vector<std::vector<T> > transform(std::vector<std::vector<T> > const &vec, 
             std::vector<std::vector<std::vector<std::vector<T> > > > const &m, 
             T z, T w) 
{ 
    std::cout << "Z" << z << "\n"; 
    std::cout << "W" << w << "\n"; 

    return vec; 
} 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    std::vector<std::vector<int> > xi; 
    std::vector<std::vector<std::vector<std::vector<int> > > > mi; 
    transform(xi,mi); 

    std::vector<std::vector<float> > xf; 
    std::vector<std::vector<std::vector<std::vector<float> > > > mf; 
    transform(xf,mf); 

    std::vector<std::vector<double> > xd; 
    std::vector<std::vector<std::vector<std::vector<double> > > > md; 
    transform(xd,md); 
} 

Salida:

Z0 
W1 
Z0 
W1.4013e-045 
Z2.122e-314 
W3.60689e-305 

así que supongo que no funciona como se esperaba !!!

Si elimina la pre-declaración y pone los argumentos predeterminados en la función de plantilla, entonces funciona como se esperaba.

#include "stdafx.h" 
#include <vector> 
#include <iostream> 

template <typename T> 
std::vector<std::vector<T> > transform(std::vector<std::vector<T> > const &vec, 
             std::vector<std::vector<std::vector<std::vector<T> > > > const &m 
             T z = T(0), T w = T(1)) 
{ 
    std::cout << "Z" << z << "\n"; 
    std::cout << "W" << w << "\n"; 

    return vec; 
} 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    std::vector<std::vector<int> > xi; 
    std::vector<std::vector<std::vector<std::vector<int> > > > mi; 
    transform(xi,mi); 

    std::vector<std::vector<float> > xf; 
    std::vector<std::vector<std::vector<std::vector<float> > > > mf; 
    transform(xf,mf); 

    std::vector<std::vector<double> > xd; 
    std::vector<std::vector<std::vector<std::vector<double> > > > md; 
    transform(xd,md); 
} 

Esto funciona como se esperaba.
Esto tiene algo que ver con que la pre-declaración de plantilla no es realmente una pre-declaración de función y, por lo tanto, no tiene parámetros predeterminados y, por lo tanto, está obteniendo valores aleatorios en la lista de parámetros.

OK. No es de mi lectura de la norma que esto debería funcionar como se esperaba:

Usando n2521
Sección 14.7.1 de instancias implícito
Párrafo 9

Una implementación no será una instancia de forma implícita una plantilla de función, miembro de una plantilla , una función de miembro no virtual, una clase de miembro o un miembro de datos estáticos de una plantilla de clase que no requiere creación de instancias. No se especifica si una implementación ejemplifica implícitamente una función miembro virtual de una plantilla de clase si la función miembro virtual no se instanciaría de otra manera. El uso de una especialización de plantilla en un argumento predeterminado no causará la creación de una instancia implícita de la plantilla, excepto que una plantilla de clase se puede instanciar cuando su tipo completo sea necesario para determinar la corrección del argumento predeterminado. El uso de un argumento predeterminado en una llamada a función hace que las especializaciones en el argumento predeterminado sean instanciadas implícitamente.

La parte en negrita del párrafo parece (a mí) para indicar que cada especialización creada por los argumentos por defecto se creará una instancia implícitamente en la unidad de traducción cuando se utiliza.

Párrafo 11:

Si una plantilla de función f se llama de una manera que requiere una expresión argumento por defecto que se utilizará, los nombres dependientes se buscan, la semántica restricciones se comprueban, y la creación de instancias de cualquier plantilla utilizada en la expresión de argumento predeterminada se realiza como si la expresión de argumento predeterminada hubiera sido una expresión utilizada en una especialización de plantilla de función con el mismo ámbito, los mismos parámetros de plantilla y el mismo acceso que la plantilla de función f utilizada en ese punto . Este análisis se llama creación de instancias de argumento por defecto. El argumento predeterminado instanciado se usa luego como el argumento de f.

Indica que, incluso si los argumentos predeterminados son parámetros de plantilla, se crearán correctamente las instancias.

Bueno, espero haberlo interpretado correctamente. :-)

+0

¿Podría tratar de llamarlo con dos especializaciones diferentes (vea mi última edición), como usar double y float? Está empezando a oler a VC++ está haciendo algo divertido aquí. – Skurmedel

+0

Jaja, querido, nunca supe que los valores predeterminados podrían ser este mal. – Skurmedel

+0

¿Alguien ha intentado esto en g ++/MinGW?Si es ilegal C++, entonces seguramente el estándar de C++ obligaría a un mensaje de error del compilador ...? –

1

No sé si esto funcionará, pero intente usar un static_cast en lugar de un elenco de estilo C para sus valores predeterminados.

* Editar: Al parecer, el problema es el compilador.

+0

produce los mismos resultados tristemente. – Skurmedel

+3

Técnicamente, no era un modelo de estilo C, sino una llamada de constructor parametrizada. –

+0

@Drew Hall: No es una llamada de constructor. – Alerty

2

¿El código está optimizado? Tal vez es por eso que el depurador te muestra los valores incorrectos.

Intenté este código más simple (en g ++ 4.3.3) y funciona como esperaba.

template <typename T> 
T increment(T a, T b = T(1)) 
{ 
    return a + b; 
} 

int main() 
{ 
    double a = 5.0; 
    std::cout << increment(a) << ", "; 
    std::cout << increment(a, 3.0) << "\n"; 
} 
+0

Muestra el mismo comportamiento tanto en la versión como en la depuración (optimización desactivada), pero comprobar si hizo una diferencia descubrí algo extraño. Solo sucede si T es doble, no si es flotante? – Skurmedel

Cuestiones relacionadas