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