代码之家  ›  专栏  ›  技术社区  ›  Skurmedel

C++模板函数得到错误的默认值

  •  10
  • Skurmedel  · 技术社区  · 14 年前

    我在C++中发现了一个真正的脑烧灼器,以前从未发生过。

    问题的要点是,在调用我的(模板)函数时,我为其定义了默认值的参数值被置乱。只有使用默认值调用函数时才会发生这种情况。

    我的模板函数声明如下:

    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) )我没有得到我期望的价值观。踏入 transform 用VC++的调试器,我看到了 z w 具有“有趣”的值(根据我的经验,这意味着有些东西没有正确初始化)。

    有趣的例子是:0.007812500000000000和2.10443116947E-317 den

    现在我试着在C++ FAQ Lite上找到答案,Google,甚至试图让舒伯特平静下来,但我不能为我的生活找出答案。我猜这真的很简单,我怀疑这是工作中的一种模版骗术。

    有没有一种方法可以得到我期望和想要的默认值,为什么它会这样对我?

    编辑1:

    如果我改变调用,那么它使用浮点数代替( transform(vector2<float>(0, 1), view_transform) )问题消失了。似乎只有在 T = double .

    编辑2:

    只有当我有两个专业 双重的 float . 如果我在一个地方使用浮动专门化,那么双重专门化会得到奇怪的默认值。如果我改变了函数被调用的所有位置,那么它使用了双倍的“消失”问题。但我还是不明白为什么,这就像在设置时使用了错误的偏移量之类的。 Z W .

    编辑3:

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

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

    如果我运行它,那么双专用化的默认参数是正确的,但是float版本得到的两个默认参数都是零(0.000000),虽然更好,但它仍然不是 z = 0 w = 1 .

    编辑4:

    做了一个 Connect issue .

    3 回复  |  直到 14 年前
        1
  •  5
  •   Loki Astari    14 年前

    对于我的In-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的参数。

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

    希望我能正确地解释。-)

        2
  •  2
  •   Matt Curtis    14 年前

    代码优化了吗?也许这就是为什么调试器向您显示错误的值。

    我尝试了这个更简单的代码(在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";
    }
    
        3
  •  1
  •   Alerty    14 年前

    我不知道这是否有效,但是尝试使用静态的强制转换而不是C样式的强制转换作为默认值。

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