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

pterodragon
  • 429
  • 8
  • 16
Gunther Piez
  • 29,760
  • 6
  • 71
  • 103

4 Answers4

30

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
  • 3,022
  • 2
  • 19
  • 36
  • 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? – Gunther Piez Mar 29 '10 at 12:44
  • 10
    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 Mar 29 '10 at 12:50
  • 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 & y.cc you use the version of A that required a dummy parameter; 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 Mar 29 '10 at 13:34
  • 2
    Sigh... Isn't complete specialization of nested template things within template class actually just partial specialization? Sometimes I appreciate I use VC++ for the most of time. Well, sometimes I feel I don't want to use C++ anymore XD – BlueWanderer Jun 08 '13 at 08:36
  • 2
    I vote with my entire lifetime savings against a C++ standard committee that makes this decision. Time to bet your career on a better language. – rwong Dec 04 '13 at 18:39
  • Too bad this is just a 'guess' and has nothing to back it up. – Adrian Jan 11 '15 at 06:07
8

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
  • 97,037
  • 24
  • 136
  • 212
  • 8
    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?__ – Gunther Piez Mar 29 '10 at 12:32
  • 2
    Only the creators of C++ Standard are really know why this is so. – Kirill V. Lyadvinsky Mar 29 '10 at 12:35
  • 10
    "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 Mar 29 '10 at 12:48
  • 5
    @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 Apr 10 '10 at 00:51
  • 4
    @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 Apr 10 '10 at 01:25
  • If a language design feature impedes the ease of reading and writing of it, and it is not a obscure language, then I think that the reason for it should be made apparent for the aggravation it causes. – Adrian Mar 22 '17 at 03:33
7

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.
  • 287,565
  • 48
  • 449
  • 722
  • 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? – Gunther Piez Mar 29 '10 at 12:54
  • 2
    Yes: as you can notice I pass the `T` to the `InnerImpl` template class. – Matthieu M. Mar 29 '10 at 14:47
  • I wonder though if this is a better solution than using a dummy template argument. – Fabio A. Dec 21 '11 at 16:10
  • @FabioA.: Good question. I prefer avoiding dummy arguments or parameters when I can because they are exposed in the API. Even if named appropriately, they still clutter it. However I am not fundamentally against the use of a dummy argument, this is more an alternative solution :) – Matthieu M. Dec 22 '11 at 12:16
4

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.

Joris Timmermans
  • 10,814
  • 2
  • 49
  • 75
  • I would argue, that TOuter::TInner is partially specialized, while TInner is completly specialzied, but an incomplete type ;-) – Gunther Piez Mar 29 '10 at 12:39
  • And assuming that you make a "semantic error" (or simply reference a non-existing symbol) in TOuter::TInner - should you get a compilation/linker error, or not? ;) (consider that TOuter may never be used...) – Virgil Mar 29 '10 at 12:44
  • 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. – Gunther Piez Mar 29 '10 at 13:06
  • 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 ;-) – Gunther Piez Mar 29 '10 at 13:08
  • 1
    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'" which is probably correct if I understand 14.7.3.2 in the standard correctly. MSVC seems to allow those specializations anyway. – Gunther Piez Mar 29 '10 at 16:44