C++模板函数获取错误的默认值

Sku*_*del 10 c++ templates default-value compiler-bug

我在C++中遇到了一个真正的大脑scorcher,它从来没有发生在我身上.

问题的关键在于,在调用my(模板)函数时,我定义的参数默认为其值加扰.只有在我使用默认值调用该函数时才会发生这种情况.

我的模板函数声明如下:

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

稍后,在相同的标题中,定义如下:

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);
}
Run Code Online (Sandbox Code Playgroud)

现在当我用defaults(transform(vector2<double>(0, 1), view_transform))调用它时,我没有得到我期望的值.进入transformVC++的调试器我看到zw有"有趣"的值(根据我的经验意味着某些东西没有正确初始化).

示例有趣的值将是:0.0078125000000000000和2.104431116947e-317 #DEN

现在我尝试在C++ FAQ Lite上找到答案,谷歌搜索它; 甚至试图用舒伯特来平息自己,但我不能为我的生活弄清楚.我猜它真的很简单,我怀疑它是某种模板tomfoolery在工作.

有没有办法获得我期望和想要的默认值,为什么它对我这样做?

编辑1:

如果我改变调用所以它使用浮点数代替(transform(vector2<float>(0, 1), view_transform))问题消失了.看起来只有在T=时才会出现double.

编辑2:

只有当我有两个专业doublefloat.如果我在一个地方使用浮动特化,则双重特化会获得奇怪的默认值.如果我改变了函数被调用的所有位置,那么它使用了两倍的问题"消失".我仍然不明白为什么,虽然,它就像它建立的时候是一个使用错误的偏移或东西zw.

编辑3:

来自C++ Crypt的故事:

#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);
}
Run Code Online (Sandbox Code Playgroud)

在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);
}

// ...
Run Code Online (Sandbox Code Playgroud)

如果我运行它,双专精化它的默认参数是正确的,但浮动版本的默认参数都是零(0.000000),虽然更好,但它仍然不是z = 0w = 1.

编辑4:

发了连接问题.

Mar*_*ork 5

我的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);
}
Run Code Online (Sandbox Code Playgroud)

输出:

Z0
W1
Z0
W1.4013e-045
Z2.122e-314
W3.60689e-305
Run Code Online (Sandbox Code Playgroud)

所以我想它没有按预期工作!

如果删除预先声明并将默认参数放在模板函数中,那么它将按预期工作.

#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);
}
Run Code Online (Sandbox Code Playgroud)

这按预期工作.
这与模板预声明实际上不是函数预声明有关,因此它实际上没有默认参数,因此您在参数列表中获得随机值.

好.不是我阅读标准,这应该按预期工作:

使用n2521
第14.7.1节隐式实例化
第9段

实现不应隐式实例化函数模板,成员模板,非虚拟成员函数,成员类或不需要实例化的类模板的静态数据成员.如果虚拟成员函数不会被实例化,则实现是否隐式实例化类模板的虚拟成员函数是未指定的.在默认参数中使用模板特化不应导致模板被隐式实例化,除了可以实例化类模板,其中需要其完整类型来确定默认参数的正确性.在函数调用中使用默认参数会导致默认参数中的特化被隐式实例化.

该段的粗体部分(对我来说)表明由于默认参数而创建的每个特化都将在使用时隐式实例化到翻译单元中.

第11段:

如果以需要使用默认参数表达式的方式调用函数模板f,则查找从属名称,检查语义约束,并且完成默认参数表达式中使用的任何模板的实例化,就好像default参数表达式是函数模板特化中使用的表达式,具有相同的作用域,相同的模板参数以及与该点使用的函数模板f相同的访问权限.此分析称为默认参数实例化.然后将实例化的默认参数用作f的参数.

表示即使默认参数是模板参数,它们也将被正确实例化.

好吧,我希望我能正确解读.:-)