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

时间:2010-07-20 23:50:23

标签: c++ templates default-value compiler-bug

我在C ++中遇到了一个真正的大脑问题,以前从未发生过这件事。

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

我的模板函数声明如下:

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

现在当我用默认值(transform(vector2<double>(0, 1), view_transform))调用它时,我没有得到我期望的值。使用VC ++的调试器进入transform我看到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);
}

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

// ...

如果我运行它,double-specialization的默认参数是正确的,但float版本的默认参数都是零(0.000000),虽然更好,但它仍然不是z = 0和{{1} }。

编辑4:

制作了Connect issue

3 个答案:

答案 0 :(得分: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);
}

输出:

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

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

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

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

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

行。不是从我对标准的阅读,这应该按预期工作:

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

  

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

段落的粗体部分(对我而言)表明由于默认参数而创建的每个专业化将在使用时隐式实例化到翻译单元。

第11段:

  

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

的参数

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

嗯,我希望我正确地解释了这一点。 : - )

答案 1 :(得分:2)

代码是否已优化?也许这就是调试器向您显示错误值的原因。

我尝试了这个更简单的代码(在g ++ 4.3.3中),它按预期工作。

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";
}

答案 2 :(得分:1)

我不知道这是否有效,但尝试使用static_cast代替C样式转换为默认值。

*编辑:显然,问题在于编译器。