44
    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
  • 421
  • 9
  • 16
Gunther Piez
  • 28,846
  • 6
  • 64
  • 101

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
  • 2,992
  • 2
  • 18
  • 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
  • 93,507
  • 24
  • 133
  • 210
  • 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" – 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.
  • 268,556
  • 42
  • 412
  • 681
  • 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,558
  • 2
  • 47
  • 73
  • 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