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

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

我在 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++ 调试器,我看到了 zw具有“有趣”的值(根据我的经验,这意味着某些内容未正确初始化)。

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

现在我尝试在 C++ FAQ Lite 上找到答案,谷歌搜索;甚至试图用舒伯特让自己平静下来,但我终究无法弄清楚。我猜它真的很简单,我怀疑这是某种模板骗局在起作用。

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

编辑 1:

如果我调用 change 所以它使用 float 代替 ( transform(vector2<float>(0, 1), view_transform) ) 问题就消失了。这似乎只有在 T 时才会发生= double .

编辑 2:

只有当我对 double 有两个特化时才会发生这种情况和 float .如果我在一个地方使用 float 特化,则 double 特化会得到奇怪的默认值。如果我更改调用该函数的所有位置,那么它会使用两倍的问题“消失”。我仍然不明白为什么,就像在设置 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);
}

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

// ...

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

编辑 4:

做了一个Connect issue .

最佳答案

我在 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段

An implementation shall not implicitly instantiate a function template, a member template, a non-virtual member func- tion, a member class or a static data member of a class template that does not require instantiation. It is unspecified whether or not an implementation implicitly instantiates a virtual member function of a class template if the virtual member function would not otherwise be instantiated. The use of a template specialization in a default argument shall not cause the template to be implicitly instantiated except that a class template may be instantiated where its complete type is needed to determine the correctness of the default argument. The use of a default argument in a function call causes specializations in the default argument to be implicitly instantiated.

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

第 11 段:

If a function template f is called in a way that requires a default argument expression to be used, the dependent names are looked up, the semantics constraints are checked, and the instantiation of any template used in the default argument expression is done as if the default argument expression had been an expression used in a function template specialization with the same scope, the same template parameters and the same access as that of the function template f used at that point. This analysis is called default argument instantiation. The instantiated default argument is then used as the argument of f.

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

好吧,我希望我的解释是正确的。 :-)

关于C++ 模板函数获取错误的默认值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3295283/

相关文章:

C++ 拉取信息并循环显示

c++ - WinAPI WC_LISTVIEW 绘制问题

具有枚举值的 Angular 5 选择和默认值

c# - 使用 DataContractSerializer 时设置属性的初始值

javascript - 将表单的所有输入重置为默认值而不重新加载

c++ - 两个排序数组实现中的第 k 个元素

c++ - 在 ctor/dtor 中启动/停止线程还是更好地使用 start()/stop()?

c++ - C++ 中的自动评估策略选择

C++ 函数指针作为模板参数

c++ - 从 Foo<Derived> 到 Foo<Base> 的转换