I have some compiler behavior - different on VisualC++ and g++ - that I don't understand (for either compiler). I will describe it in English but maybe it would be easier just to look at the code below.
It has to do with a class template that has a member class template and a member function template. I am trying to simultaneously
I find that, with both compilers, everything compiles fine (and instantiations are as expected) if I do either #1 (explicitly specialize outer class template) or #2 (explicitly specialize member templates).
But if I try to do both #1 and #2 simultaneously (declared in the correct order I believe), I find that
Here is the outer class template primary definition. It is the same in all of the cases below:
// Class template with a member class template and a member function template
template <int I>
struct OuterClass
{
template <int J> struct InnerClass {};
template <int J> static void InnerFunc() {}
};
Here is doing #1 (explicitly specialize outer class template) only. This compiles fine and instantiations are as expected.
// Explicit specialization of outer class template,
// leaving member templates unspecialized.
template <>
struct OuterClass<1>
{
template <int J> struct InnerClass {};
template <int J> static void InnerFunc() {}
};
Here is doing #2 (explicitly specialize member templates) only. This compiles fine and instantiations are as expected.
// Explicit specialization of inner templates for
// an explicit specialization of outer class template
template <> template <> struct OuterClass<1>::InnerClass<1> {};
template <> template <> void OuterClass<1>::InnerFunc<1>() {}
Here is trying to do both #1 and #2 simultaneously - just pasting the two previous code snippets together:
// Explicit specialization of outer class template,
// leaving member templates unspecialized.
template <>
struct OuterClass<1>
{
template <int J> struct InnerClass {};
template <int J> static void InnerFunc() {}
};
// Explicit specialization of inner templates for
// an explicit specialization of outer class template
template <> template <> struct OuterClass<1>::InnerClass<1> {}; // Line A
template <> template <> void OuterClass<1>::InnerFunc<1>() {} // Line B
g++ compiles "Line A" fine (and instantiations are as expected). But g++ gives a compiler error for Line B: "too many template-parameter-lists".
VC++ gives compiler errors for both "Line A" and "Line B" (too messy and numerous to recount here).
Again, both "Line A" and "Line B" compile fine, for both compilers, when they do not appear after the explicit specialization of the outer class template.
In my understanding, everything should compile fine. So who is right - me, g++, or VC++? And more importantly, why?
Please understand this is not a "How do I accomplish X" question, this is a "I want to fully understand C++" question. If you take the time to read through this and think about it you have my thanks...I hope I boiled it down as much as possible.
I spent a ridiculous amount of time on this issue and I guess I got it figured out - where by "figured out" I mean I know exactly what will and will not compile on both the compilers I've tried (MSVC and g++), and what syntax to use for each compiler. This is all ugly but it is predictable and repeatable - I have a lot of example code not shown here from which I deduced the results. To be clear and avoid frustration on my part, the description here is not my theory it is observed behavior of the compilers across hunderds of example cases.
At a high level:
The detailed description I give here is probably too brief to be understood by the casual reader, but I'll try anyway.
Here is some example code, showing how many times "template<>" must appear when defining a nested class template with a particular specialization chain. This compiles on both MSVC and g++ (using conditional compilation). This code does not involve nested function templates.
#include <boost\predef.h> // For conditional compilation
// Nested class templates, 3 deep.
template <int I1> struct T1
{
template <int I2> struct T2
{
template <int I3> struct T3 {};
};
};
// Specialization of the third level of class template.
// "template<>" appears three times here for both MSVC and g++ -
// in this case the rules for both compilers both yield 3.
// Note this class template specialization nests another 2 levels of class templates.
template <> template <> template<> struct T1<1>::T2<1>::T3<1>
{
template <int I4> struct T4
{
template <int I5> struct T5 {};
};
};
// Specialize the class template contained in the class template specialization above.
// In this case, the number of times "template<>" must appear differs between MSVC and g++,
// so conditional compilation is used.
#if BOOST_COMP_GNUC
// According to the rule described for g++, "template<>" must appear 4 times:
// (Overall specialization level of 5) - (1 covering specialization which is T1<1>::T2<1>::T3<1>) = 4
template <> template<> template<> template<> struct T1<1>::T2<1>::T3<1>::T4<1>::T5<1>
#elif BOOST_COMP_MSVC
// MSVC uses the last hop specialization rule, so "template<>" must appear 2 times -
// because the closest covering specialization, T1<1>::T2<1>::T3<1>, is two hops back.
template <> template<> struct T1<1>::T2<1>::T3<1>::T4<1>::T5<1>
#else
#error Unsupported compiler!
#endif
{
//...
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With