Pregunta

Me encontré con un verdadero problema en C++, nunca me había sucedido antes.

La esencia del problema es que al invocar mi función (plantilla), los argumentos para los que he definido los valores predeterminados tienen sus valores codificados.Sólo sucede 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));

Más adelante, en el mismo encabezado, se 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)) No obtengo los valores que espero.Entrando en transform con el depurador de VC++s veo z y w tener valores "divertidos" (lo que en mi experiencia significa que algo no se inicializa correctamente).

Ejemplos de valores divertidos serían:0.0078125000000000000 y 2.104431116947e-317#DEN

Ahora intenté encontrar la respuesta en C++ FAQ Lite, buscándola en Google;Incluso intenté calmarme con Schubert, pero por mi vida no puedo entenderlo.Supongo que es muy simple y sospecho que es una especie de plantilla de tontería en funcionamiento.

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

Edición 1:

Si hago el cambio, llamo para que use flotadores en su lugar (transform(vector2<float>(0, 1), view_transform)) el problema desaparece.Parece que esto sólo ocurre si T = double.

Edición 2:

Solo sucede si tengo dos especializaciones para double y float.Si uso una especialización flotante en un lugar, la especialización doble obtiene valores predeterminados extraños.Si cambio todos los lugares donde se llama a la función, usa el doble, los problemas "desaparecen".Todavía no entiendo por qué, es como si estuviera usando compensaciones defectuosas o algo así al configurar z y w.

Edición 3:

Cuentos de la cripta de C++:

#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 matriz4.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 ejecuto eso, la especialización doble tiene sus argumentos predeterminados correctos, pero la versión flotante obtiene ambos argumentos predeterminados como cero (0.000000), lo cual, aunque es mejor, todavía no lo es. z = 0 y w = 1.

Edición 4:

Hecho una Problema de conexión.

¿Fue útil?

Solución

Lo siguiente falla 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);
}

Producción:

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

¡¡¡Así que supongo que no funciona como se esperaba!!!

Si elimina la declaración previa y coloca 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 declaración previa de la plantilla no es en realidad una declaración previa de función y, por lo tanto, en realidad no tiene parámetros predeterminados y, como tal, obtiene valores aleatorios en la lista de parámetros.

DE ACUERDO.Según mi lectura del estándar, esto debería funcionar como se esperaba:

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

Una implementación no deberá instanciar implícitamente una plantilla de función, una plantilla de miembro, 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 requiera instanciación.No se especifica si una implementación crea implícitamente una instancia de una función miembro virtual de una plantilla de clase si de otro modo no se crearía una instancia de la función miembro virtual.El uso de una especialización de plantilla en un argumento predeterminado no causará que se cree una instancia implícita de la plantilla, excepto que se pueda crear una instancia de una plantilla de clase cuando se necesite su tipo completo para determinar la exactitud del argumento predeterminado. El uso de un argumento predeterminado en una llamada de función hace que se creen instancias implícitas de las especializaciones en el argumento predeterminado.

La parte en negrita del párrafo parece (para mí) indicar que cada especialización creada debido a argumentos predeterminados se instanciará implícitamente en la unidad de traducción cuando se use.

Párrafo 11:

Si se llama a una plantilla de función f de una manera que requiere el uso de una expresión de argumento predeterminada, se buscan los nombres dependientes, se verifican las restricciones semánticas 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 había sido una expresión utilizada en una especialización de plantilla de función con el mismo alcance, los mismos parámetros de plantilla y el mismo acceso que el de la plantilla de función f utilizada en ese momento.Este análisis se denomina creación de instancias de argumentos predeterminados.El argumento predeterminado instanciado se utiliza luego como argumento de f.

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

Bueno, espero haberlo interpretado correctamente.:-)

Otros consejos

¿Está optimizado el código?Quizás es por eso que el depurador le muestra valores incorrectos.

Probé este código más simple (en g++ 4.3.3) y funciona como se 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";
}

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

*Editar: Al parecer el problema es el compilador.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top