I have a class template nested inside another template. Partially specializing it is easy: I just declare another template< … >
block inside its parent.
However, I need another partial specialization that happens to specify all its local template arguments. This makes it into an explicit specialization. Explicit specializations, for whatever reason, must be at namespace scope. To declare it outside its parent class, the parent must be nominated, which requires a non-empty template argument list. This implies partial specialization. Partial specialization is what I'm doing, and it's supposed to work at arbitrary outer scope. But both GCC and Comeau fail to identify the template parameter in the parent nomination with the partial specialization formal arguments.
template< class X > struct A {
template< class Y > struct B; // initial declaration OK
template< class Z >
struct B< A< Z > > {}; // partial OK as long as there's a local arg
template<> // ERROR: this syntax triggers explicit specialization
struct B< int > {};
};
template<> // ERROR: can't nest template<>s here (why?)
template< class X > // ERROR: can't deduce X from type of A<X>::B<int> (why?)
struct A< X >::B< int > {};
(I left all my non-working code in; comment it appropriately to attempt to make sense.)
It is illegal under C++ standard 14.7.3/18:
.... the declaration shall not explicitly specialize a class member
template if its enclosing class templates are not explicitly specialized
as well.
I tend not to use nested classes too much. My main complaint is that they have a tendency to bloat the code of the class they are nested in.
I would therefore propose another workaround:
namespace detail
{
template <class X, class Z> class BImpl;
template <class X, class Z> class BImpl<X, A<Z> > {};
template <class X> class BImpl<X,int> {};
}
template <class X>
class A
{
template <class Z> struct B: BImpl<X,Z> {};
};
Just note that it requires to pass X as an argument to BImpl
if ever you wish to also specialize A
. Funny thing is that in this case, I end up with only partial specialization!
Complex stuff. Your initial code ICE's VC10 Beta2, nice.
First off, I think you have this backwards:
template<>
template< class X >
struct A< X >::B< int > {};
X is a template param to struct A, and B is the one fully specialized, so I think it should be this:
template< class X >
template<>
struct A< X >::B< int > {};
But even this fails to compile. The error text is actually useful, though:
a.cpp a.cpp(11) : error C3212:
'A::B' : an explicit
specialization of a template member
must be a member of an explicit
specialization
a.cpp(8) : see declaration of 'A::B'
It looks like it is only legal to fully specialize B if you also fully specialize A.
Edit: Ok, I heard back from someone who can speak authoritatively on this - to paraphrase, this is a very murky area in the standard, and it's an open issue with the C++ Committee to clean it up ("it" being explicit specializations of members of class templates). In the near term, the advice is "Don't do that".
Atleast this works in VC 2010. But, I am unable to write the def. of fun() for "int" outside the class declaration.
EDIT: Unfortunately g++ has also compilations issues.
EDIT: The code below worked on VC 2010.
template<typename X>
class A
{
public:
A()
{
}
template<typename Y>
struct B
{
void fun();
};
template<>
struct B<int>
{
void fun()
{
cout << "Specialized version called\n";
}
//void fun();
};
public:
B<X> b;
};
template<typename X>
template<typename Y>
void A<X>::B<Y>::fun()
{
cout << "templated version called\n";
}
int main()
{
A<int> a;
a.b.fun();
A<float> a1;
a1.b.fun();
}