Thursday, January 6, 2011

Interesting Templating Conundrum

So many areas of C++ are yet unexplored by me...

I think I am finally beginning to understand the logic behind many of the very confusing template rules in the C++ standard (though I also think I am a far way off from full zen-like understanding). I was faced with a difficult problem today and with the help of the good people of ##c++ I was able to arrive at a solution.

Take a look at the code below. A couple of errors where thrown by the compiler. Can you figure out what's going on?

Erroneous Code
template<class T> struct A { template<class Value> void foo() { } }; template<class T> void bar() { A<T> i; // error: expected primary-expression before ‘>’ token // error: expected primary-expression before ‘)’ token i.foo<T>(); } template<class T> void qux() { A<int> i; // No error i.foo<T>(); } int main() { return 0; }

I will give you a hint by transforming the code slightly. The elements involved are treated the same by the compiler, but conceptually it is a bit easier to grasp.

Transformed Erroneous Code
template<int T> struct A { template<int Value> void foo() { } }; template<int T> void bar() { A<T> i; // error: expected primary-expression before ‘)’ token i.foo<T>(); } template<int T> void qux() { A<1> i; // No error i.foo<T>(); } int main() { return 0; }

On line 14, you'll see the offending statement: i.foo<T>(). Now let's rewrite it a different way, the way the the compiler is seeing it: i.foo < T > ( /* Error: Missing Value! );. Since T is of type int, this code could very easily be comparing some function's (i.foo) address in memory* to the parameter T, and then comparing that result (a boolean value now) to some value (which is missing, thus the error). It isn't an obvious ambiguity but it is there nonetheless.

This ambiguity gets carried over to very similar circumstances, like the first example, where an ambiguity doesn't actually exist, but since it is so similar, it ends up violating the same rule. Truthfully this logic doesn't totally make sense to me, I feel as if an error should only be thrown in the second case, however, I did not write the standard, and I'm sure the committee had some reason to include both cases. Note: This might be a trait of just G++ which is the compiler I use. If anyone could check to see if both cases error on their compiler I would appreciate it.

Now that we know what the problem is, how do we go about solving it? Observe the code below, but be warned, it is not for the faint of heart.

Good Code
template<class T> struct A { template<class Value> void foo() { } }; template<class T> void bar() { A<T> i; i.template foo<T>(); } template<class T> void qux() { A<int> i; i.foo<T>(); } int main() { return 0; }

And there we have it. A very strange error with a very strange solution. I am still grappling with the intricacies of C++ templates, but I feel as if every day I get closer and closer to fully understanding them.

*: You might wonder why the qux() function has no errors then, and so do I. I believe that it is due to the fact that i is non-dependent in the qux() function, but is dependent In the foo() function. So the compiler knows that the function foo<T>() exists in A when we are accessing qux's i, but not when we are accessing bar's i. The amount of sense this makes is limited. Especially since when I added a simple void foo() non-templated function to A the qux function still compiled and ran normally. If you're interested and find out the logic behind all of this, please tell me, for I am still moderately confused by it all.