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

发布于 2024-09-10 09:20:09 字数 2877 浏览 2 评论 0 原文

我在 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(0, 1), view_transform) )我没有得到我期望的值。使用 VC++ 调试器进入 transform 时,我看到 zw 具有“有趣”的值(根据我的经验,这意味着某些东西不是已正确初始化)。

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

现在我尝试在 C++ FAQ Lite 上找到答案,谷歌搜索它;我什至试图通过舒伯特来让自己平静下来,但我一生都无法弄清楚。我猜这真的很简单,而且我怀疑这是某种模板的愚蠢行为。

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

编辑 1:

如果我进行更改调用,那么它会使用浮点数 (transform(vector2(0, 1), view_transform)),问题就会消失。看来只有当 T = double 时才会发生这种情况。

编辑2:

只有当我有doublefloat两个专业化时才会发生这种情况。如果我在一个地方使用浮点专业化,则双重专业化会得到奇怪的默认值。如果我更改调用该函数的所有位置,那么它使用双倍,问题就会“消失”。我仍然不明白为什么,就像在设置 zw 时使用了错误的偏移量或其他东西。

编辑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:

做了一个连接问题

I have hit upon a real brain scorcher in C++, it has never happened to me before.

The gist of the problem is that upon invocation of my (template) function the arguments I have defined defaults for have their values scrambled. It only happens if I call the function with the defaults.

My template function is declared like this:

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

It is later, in the same header, defined like this:

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

Now when I call this with defaults (transform(vector2<double>(0, 1), view_transform)) I don't get the values I expect. Stepping into transform with VC++s debugger I see z and w having "funny" values (which in my experience means something isn't initialized properly).

Example funny values would be: 0.0078125000000000000 and 2.104431116947e-317#DEN

Now I've tried finding the answer on C++ FAQ Lite, googling it; even tried to calm myself with Schubert, but I can't for the life of me figure it out. I'm guessing it's really simple and I suspect it's some kind of template tomfoolery at work.

Is there a way to get the default values I expect and want, and why does it do this to me?

Edit 1:

If I the change call so it uses floats instead (transform(vector2<float>(0, 1), view_transform)) the problem goes away. It appears this only occurs if T = double.

Edit 2:

It only happens if I have two specializations for double and float. If I use a float specialization in one place the double specialization gets weird default values. If I change all the places the function is called so it uses double the problems "goes away". I still don't understand why though, it's like it's using faulty offsets or something when setting up z and w.

Edit 3:

Tales from the 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);
}

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

// ...

If I run that, the double-specialization has it's default arguments correct, but the float version gets both it's default arguments as zero (0.000000) which albeit better, it's still is not z = 0 and w = 1.

Edit 4:

Made a Connect issue.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

北方。的韩爷 2024-09-17 09:20:09

我在 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 的参数。

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

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

The following fails for my 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);
}

Output:

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

So I suppose it does not work as expected!!!

If you remove the pre-declaration and put the default arguments in the template function then it works as expected.

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

This works as expected.
This has something to do with the template pre-declaration not actually being a function pre-declaration and thus it does not actually have default parameters and as such you are getting random values in the parameter list.

OK. Not from my reading of the standard this should work as expected:

Using n2521
Section 14.7.1 Implicit instantiation
Paragraph 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.

The bold part of the paragraph seems (to me) to indicate that each specialization created because of default arguments will be implicitly instantiated into the translation unit when used.

Paragraph 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.

Indicates that even if the default arguments are template parameters they will be correctly instantiated.

Well I hope I interpreted that correctly. :-)

心凉 2024-09-17 09:20:09

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

我尝试了这个更简单的代码(在 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";
}

Is the code optimized? Maybe that's why the debugger is showing you the wrong values.

I tried this simpler code (in g++ 4.3.3) and it works as expected.

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";
}
故事和酒 2024-09-17 09:20:09

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

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

I do not know if this will work, but try using a static_cast instead of a C style cast for your default values.

*Edit: Apparently, the problem is the compiler.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文