我在 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
时,我看到 z
和 w
具有“有趣”的值(根据我的经验,这意味着某些东西不是已正确初始化)。
示例有趣的值是: 0.0078125000000000000 和 2.104431116947e-317#DEN
现在我尝试在 C++ FAQ Lite 上找到答案,谷歌搜索它;我什至试图通过舒伯特来让自己平静下来,但我一生都无法弄清楚。我猜这真的很简单,而且我怀疑这是某种模板的愚蠢行为。
有没有办法获得我期望和想要的默认值,为什么它对我这样做?
编辑 1:
如果我进行更改调用,那么它会使用浮点数 (transform(vector2(0, 1), view_transform)
),问题就会消失。看来只有当 T
= double
时才会发生这种情况。
编辑2:
只有当我有double
和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);
}
在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 = 0
和 w = 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.
发布评论
评论(3)
我在 Dev Studio 中出现以下错误:
输出:
所以我认为它不能按预期工作!
如果删除预声明并将默认参数放入模板函数中,那么它会按预期工作。
这按预期工作。
这与模板预声明实际上不是函数预声明有关,因此它实际上没有默认参数,因此您在参数列表中获得随机值。
好的。不是从我对标准的阅读来看,这应该按预期工作:
使用 n2521
第 14.7.1 节 隐式实例化
第 9 段
该段落的粗体部分(对我来说)似乎表明由于默认参数而创建的每个特化将使用时隐式实例化到翻译单元中。
第 11 段:
指示即使默认参数是模板参数,它们也会被正确实例化。
好吧,我希望我的解释是正确的。 :-)
The following fails for my in Dev Studio:
Output:
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.
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
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:
Indicates that even if the default arguments are template parameters they will be correctly instantiated.
Well I hope I interpreted that correctly. :-)
代码优化了吗?也许这就是调试器向您显示错误值的原因。
我尝试了这个更简单的代码(在 g++ 4.3.3 中)并且它按预期工作。
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.
我不知道这是否有效,但尝试使用 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.