views:

396

answers:

4
    template<int x> struct A {                                                                                                    
        template<int y> struct B {};.                                                                                             
        template<int y, int unused> struct C {};                                                                                  
    };                                                                                                                            

    template<int x> template<> 
    struct A<x>::B<x> {}; // error: enclosing class templates are not explicitly specialized

    template<int x> template<int unused> 
    struct A<x>::C<x, unused> {}; // ok

So why is the explicit specialization of a inner, nested class (or function) not allowed, if the outer class isn't specialized too? Strange enough, I can work around this behaviour if I only partially specialize the inner class with simply adding a dummy template parameter. Makes things uglier and more complex, but it works.

I would consider complete specializations as a subset of partial specializations - especially because you can express every complete specialization as a partial with adding a dummy parameter. So this disambiguation between partial and full specialization doesn't really make sense for me.

Unfortunatly nobody at comp.std.c++ dared to answer, so I am putting it up here again with a bounty.

Note: I need this feature for recursive templates of the inner class for a set of the outer class and the specialization of the inner parameter does depend on the outer template parameter.

+3  A: 

C++ Standard explicitly prohibits full specialization of member template classes in first case. According to 14.7.3/18:

In an explicit specialization declaration for a member of a class template or a member template that appears in namespace scope, the member template and some of its enclosing class templates may remain unspecialized, except that the declaration shall not explicitly specialize a class member template if its enclosing class templates are not explicitly specialized as well.

Kirill V. Lyadvinsky
This is not the answer of my question. If you would have read my question, you would have noted that I already know that explicit specialization of a nested class is not allowed. And I already found a way to circumvent this prohibition. My question was __Why is this so?__
drhirsch
Only the creators of C++ Standard are really know why this is so.
Kirill V. Lyadvinsky
Maybe :-) But I hoped that a SO reader did know it too :-)
drhirsch
"Only the creators of C++ Standard are really know why this is so" - what makes you say that? The C++ standard was developed through an open process, and many decisions were debated in public: on mailing lists or newsgroups, at meetings. Furthermore, Stroustrup has written a book about some of the design decisions behind C++. Have you checked that there is no available information on this particular decision, or is this a philosophical argument about what anyone can "really know" about anything? ;-) Language design is not a black box where "experts" walk in, and standards come out.
Steve Jessop
@Steve, "The C++ standard was developed through an open process" <- that's only what some committee members wants to make people believe. It's not really open, unfortunately. The committee mailing list (the "reflector"), where apparently most of the work is done, is kept closed from the public.
Johannes Schaub - litb
@litb: true, and if in the early days Andrew Koenig and Bjarne Stroustrup met in a lift, and nobody took minutes, then who knows what dark deals might have been done. My point is not that C++ is perfectly transparent, just that Kirill's absolute statement is desperately unambitious, considering that in many cases decisions relating to C++ have been aired in public ad nauseam. I have no idea whether this is one of them, I just tire of the idea that language design is something programmers have no business trying to think about or be curious about. "Return to work, labour-unit drhirsch" ;-)
Steve Jessop
+5  A: 

My guess as to why this happens: complete specializations are no longer "template classes/functions", they are are "real" classes/methods, and get to have real (linker-visible) symbols. But for a completely-specialized template inside a partially-specialized one, this would not be true. Probably this decision was taken just to simplify the life of compiler-writers (and make life harder for coders, in the process :P ).

Virgil
This seems a good reason. But OTOH, couldn't the compiler writers do the same thing as I did with adding an invisible, unused dummy parameter?
drhirsch
That's why I make the joke in parenthesis. It's clear that there is no hard reason like "it could never ever work because [...]", so the best conceivable reason why it was disallowed is "not to confuse the users".But that would be funny, considering in how many other ways a n00b C++ programmer may get confused :). I actually found that C++ does an excellent job both in confusing the unsuspecting novice AND in preventing the very-advanced programmer from doing valid (and sometimes even elegant) work, all for apparently tiny implementation details. But that's just my opinion, of course :)
Virgil
To be fair, there are other implications that you haven't considered.Say you define A as you did, and the compiler adds an extra dummy parameter. But since it's a template it gets included in multiple .cpp files (but not ALL of them!). Say you end up in the situation where in files x.cc while in the file z.cc you end up using a completely different version of A - which is a template but has two type arguments from the start (and doesn't require the dummy). How do you tell the difference at link time?
Virgil
+3  A: 

Backing up Virgil's argument (he was faster than I posting the same rationale), consider this:

template<typename T1>
class TOuter
  {
  public:
    template<typename T2>
    class TInner
      {
      public:
        T1 m_1;
        T2 m_2;
      };
  };

template<typename T1>
template<>
class TOuter<T1>::TInner<float>
  {
  public:
    T1    m_1;
    float m_2;
 };

Is TInner fully specialized, or partially specialized because of T1?

Edit:

After considering some of the other comments - it seems that you want to have a full specialization based on the template parameter in the outer class. If you nest the inner class's implementation, that seems to work in Visual Studio 2005:

template<typename T1>
class TOuter
  {
  public:
    template<typename T2>
    class TInner
      {
      public:
        std::string DoSomething() { return "Inner - general"; }
        T2 m_2;
      };

    template<>
    class TInner<T1>
      {
      public:
        std::string DoSomething() { return "Inner - special"; }
        T1 m_1;
      };
  };

TOuter::TInner will correctly be the specialization of TInner. I could not get it to compile with the implementation outside of the template.

MadKeithV
I would argue, that TOuter<T1>::TInner<float> is partially specialized, while TInner<float> is completly specialzied, but an incomplete type ;-)
drhirsch
And assuming that you make a "semantic error" (or simply reference a non-existing symbol) in TOuter<T1>::TInner<float> - should you get a compilation/linker error, or not? ;)(consider that TOuter may never be used...)
Virgil
I had exactly this case in the real code - a wrongly typed constructor in the specialized inner class. (I am using the partial specialization circumvention I described in the beginning.) The error only shows up, if a variable of type TOuter is declared.
drhirsch
And in some member function a variable of type TInner is used... Anyway I would expect the compiler to magically extend the template parameter list with an dummy parameter, so that I don't need to do it ;-)
drhirsch
About your edit: Yes, the specialization depends on a parameter of the outer template. Actually your code was the very first way I tried - but gcc refuses to translate it with a "error: explicit specialization in non-namespace scope 'class TOuter<T1>'" which is probably correct if I understand 14.7.3.2 in the standard correctly. MSVC seems to allow those specializations anyway.
drhirsch
+2  A: 

You can work around this behavior by delegating the real work to another structure though:

namespace detail
{
  template <class T, class U>
  struct InnerImpl {};
}

template <class T>
struct Outer
{
  template <class U>
  struct Inner: detail::InnerImpl<T,U>
  {
  };
};

Now you can specialize InnerImpl as you wish

Matthieu M.
Interesting idea. But would it work for me, because I need the specialization of the inner class depend on the template parameter of the outer class?
drhirsch
Yes: as you can notice I pass the `T` to the `InnerImpl` template class.
Matthieu M.