views:

258

answers:

4

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

A: 

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

Terry Mahaffey
Yes, I noticed I'd posted the nested argument lists "backwards"; I tried both ways until I got sick. One way it doesn't see X and the other way it complains about explicit inside partial. Also depending on which compiler used, of course. The standard is very vague and disorganized about this stuff, but I don't see any reason it's illegal.
Potatoswatter
I see. I am not a language lawyer (although I sometimes play one on the internets) - but VC10 is highly conformant, so if it tells me something is illegal, I tend to believe it. Hopefully someone else can jump in with the language-ese describing this behavior.
Terry Mahaffey
A: 

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();
}
Jagannath
That's good to know. I'd say there's no *good* reason the compilers shouldn't all support this if one does. I don't want to write the definition outside the class, anyway. But, of course, this was the first thing I tried…
Potatoswatter
+6  A: 

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.

Thanks! I know I've been through this before, because it applies to all template recursion.
Potatoswatter
+2  A: 

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!

Matthieu M.
Yep. My preference is for bloated classes rather than bloated external representation with `friend`s… the total amount of code is the same, or less with nesting. In this particular case, the parent class had a C++0x variadic parameter list and it was trying to pass a subset to the child, which is hence also variadic. Therefore, no way of passing all the parent's arguments to the child/friend and yet explicitly specializing some case of the child/friend.
Potatoswatter