Why Is Partial Specialization of a Nested Class Template Allowed, While Complete Isn'T

Why is partial specialization of a nested class template allowed, while complete isn't?

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

Partial specialization of a nested class

If you do not want to specialize whole class then just move the iterator out of class and make it template:

template<class T, int N>
class C_iterator
{
...
};

If needed make your specializations:

template<class T>
class C_iterator<T, 1>
{
...
};

Then use it in your class as iterator, if needed befriend it:

template<class T, int N>
class C
{
using iterator = C_iterator<T, N>;
friend iterator;
...
};

Understanding partial specialization of inherited nested class templates

A partial specialization must redeclare the same name as the primary template for which it provides an alternative definition.

When you write struct Inner within the scope of Derived, you are declaring Derived::Inner. Base::Inner is a distinct name from Derived::Inner and therefore declares a different class. It is not possible to specialize Base::Inner with a declaration that declares Derived::Inner.

When you write Derived::Inner at namespace scope, name lookup resolves that name to Base::Inner - the specializations are all of the same class: Base::Inner, even if you refer to them as Derived::Inner.

From the standard:

[temp.class.spec]

A partial specialization of a class template provides an alternative definition of the template that is used instead of the primary definition when the arguments in a specialization match those given in the partial specialization.

Nested class template specialization

It's forbidden to create explicit specialization in class-scope:

An explicit specialization shall be declared in a namespace enclosing
the specialized template.

But it's not forbidden to create partial specialization:

A class template partial specialization may be declared or redeclared
in any namespace scope in which its definition may be defined (14.5.1
and 14.5.2).

this

template <bool Dummy>
class Nested<int, Dummy>{}; // why need to provide an argument??

is partial specialization and it's allowed to create such specialization in class-scope. You also cannot fully specialize nested class, in not-specialized outer class. You can do this:

template<>
template<>
class A<int, double>::Nested<int>
{
};

but you cannot do

template<typename C, typename T>
template<>
class A<C, T>::Nested<int>
{
};

Partial specialization with type nested in a templated class

The answer is that you cannot do this specialization. It is not a syntax error, but just something that cannot be realized. You have to see template specializations a little bit like function overloading. The compiler has to take the type argument at the use-site, look at the specializations available, find matches, and select the best one (most specialized one). The problem with your example is that the "find match" step cannot be realized with such a specialization. The compiler can expect "nested_type" to be anything, not necessarily a unique type (as it is in your example), it could also be a nested typedef, for instance. Moreover, the compiler cannot predict that it is already seeing all the specializations of template "y", so even if nested_type is a unique type nested in y (general template), it could be a nested typedef in an upcoming template specialization declaration for template "y".

Just like with function overloading and the matching algorithm used there, the compiler is limited in its capabilities to deduce the type, and what limits it is how much assumptions it can make. If you have a specialization for x<int> and later use x<int>, the match is trivial, no deduction needed, no assumptions needed. If you have a specialization like x<T*> and later use x<int*>, the match is easy, T can be deduced to be int. If you have a specialization like x< y<T>::type > and then use any version of x, how is the compiler supposed to deduce T from y::type? It would have to substitute for T in y all the possible types that exist in the entire world to see if there is one that results in a matching nested type. That's an unreasonable expectation, and that is why the type deduction capabilities of C++ templates stop here. Very often, to know if you should expect the compiler to be able to resolve something, just put yourself in its shoes and see if it is even remotely possible (the answer is usually clear).

Weird nested class partial specialization results on both gcc and clang

Your code is correct; out-of-class implicitly instantiated class template member class template partial specializations are intended to be allowed by the Standard, as long as they are defined early enough.

First, let's try for a minimal example - noting by the way that there's nothing here that requires C++11:

template<class T> struct A {
template<class T2> struct B { };
};
// implicitly instantiated class template member class template partial specialization
template<> template<class T2>
struct A<short>::B<T2*> { };
A<short>::B<int*> absip; // uses partial specialization?

As noted elsewhere MSVC and ICC use the partial specialization as expected; clang selects the partial specialization but messes up its type parameters, aliasing T2 to short instead of int; and gcc ignores the partial specialization entirely.

Why out-of-class implicitly instantiated class template member class template partial specialization is allowed

Put simply, none of the language that permits other forms of class template member class template definitions excludes out-of-class implicitly instantiated class template member class template partial specialization. In [temp.mem], we have:

1 - A template can be declared within a class or class template; such a template is called a member template. A
member template can be defined within or outside its class definition or class template definition. [...]

A class template partial specialization is a template declaration ([temp.class.spec]/1). In the same paragraph, there is an example of out-of-class nonspecialized class template member class template partial specialization ([temp.class.spec]/5):

template<class T> struct A {
struct C {
template<class T2> struct B { };
};
};
// partial specialization of A<T>::C::B<T2>
template<class T> template<class T2>
struct A<T>::C::B<T2*> { };
A<short>::C::B<int*> absip; // uses partial specialization

There is nothing here to indicate that the enclosing scope cannot be an implicit specialization of the enclosing class template.

Similarly, there are examples of in-class class template member class template partial specialization and out-of-class implicitly instantiated class template member class template full specialization ([temp.class.spec.mfunc]/2):

template<class T> struct A {
template<class T2> struct B {}; // #1
template<class T2> struct B<T2*> {}; // #2
};
template<> template<class T2> struct A<short>::B {}; // #3
A<char>::B<int*> abcip; // uses #2
A<short>::B<int*> absip; // uses #3
A<char>::B<int> abci; // uses #1

(clang (as of 3.7.0-svn235195) gets the second example wrong; it selects #2 instead of #3 for absip.)

While this does not explicitly mention out-of-class implicitly instantiated class template member class template partial specialization, it does not exclude it either; the reason it isn't here is that it's irrelevant for the particular point being made, which is about which primary template or partial template specializations are considered for a particular specialization.

Per [temp.class.spec]:

6 - [...] when the primary
template name is used, any previously-declared partial specializations of the primary template are also
considered.

In the above minimal example, A<short>::B<T2*> is a partial specialization of the primary template A<short>::B and so should be considered.

Why it might not be allowed

In other discussion we've seen mention that implicit instantiation (of the enclosing class template) could result in implicit instantiation of the definition of the primary template specialization to take place, resulting in an ill-formed program NDR i.e. UB; [templ.expl.spec]:

6 - If a template, a member template or a member of a class template is explicitly specialized then that specialization
shall be declared before the first use of that specialization that would cause an implicit instantiation
to take place, in every translation unit in which such a use occurs; no diagnostic is required. [...]

However, here the class template member class template is not used before it is instantiated.

What other people think

In DR1755 (active), the example given is:

template<typename A> struct X { template<typename B> struct Y; };
template struct X<int>;
template<typename A> template<typename B> struct X<A>::Y<B*> { int n; };
int k = X<int>::Y<int*>().n;

This is considered problematic only from the point of view of the existence of the second line instantiating the enclosing class. There was no suggestion from the submitter (Richard Smith) or from CWG that this might be invalid even in the absence of the second line.

In n4090, the example given is:

template<class T> struct A {
template<class U> struct B {int i; }; // #0
template<> struct B<float**> {int i2; }; // #1
// ...
};
// ...
template<> template<class U> // #6
struct A<char>::B<U*>{ int m; };
// ...
int a2 = A<char>::B<float**>{}.m; // Use #6 Not #1

Here the question raised is of precedence between an in-class class template member class template full specialization and an out-of-class class template instantiation member class template partial specialization; there is no suggestion that #6 would not be considered at all.

Template specializations for inner class

The simple answer - you can't fully specialize templated inner class of templated outer class. But if you really want to achieve similar effect you could try partial specialization with dummy defaulted template parameter:

#include <iostream>

template<typename A>
struct X
{
template <typename T, T* =nullptr>
class Y{};
};

template<typename A>
template<double *Ptr>
class X<A>::Y<double, Ptr> {
public:
static constexpr int value = 1;
};

int main() {
std::cout << X<int>::Y<double>::value << std::endl;
}

[live demo]

something confusing about class template partial specialization & class member specialization

The explicit specialization of Foo<double, int>::bar

template<>
template<>
void Foo<double, int>::bar(double) {
std::cout << "Now I'm specialized!\n";
}

causes an implicit instantiation of Foo<double, int>. This is a better match than the partially specialized Foo<T, int>, so you get Foo_0 instead of Foo_1, unless you comment out the specialization of bar.

What you could do is to move bar(double) into the general class template Foo<T, S> as a regular overloaded member function

template<class T, class S>
class Foo {
// as before

void bar(double) {
std::cout << "Now I'm overloaded!\n";
}
};

Now you will get Foo_1, live example. Note that you won't be able to call Foo<double, int>::bar(double) anymore. If you want that, then you need to add a bar member to the partial specialization Foo<T, int> as well.

Partial Template Specialization: Why does some of the variables in the partial specialization list different from the primary template

In a template specialization, the name you choose for your template arguments are silent-variables. All of the following is exactly equivalent:

template<class T, class U, int N> class A<T, U, N> { /*...*/ };
template<class U, class T, int N> class A<U, T, N> { /*...*/ };
template<class F, class G, int Z> class A<F, G, Z> { /*...*/ };
template<class OnceUpon, class ATime, int ThereWere> class A<OnceUpon, ATime, ThereWere> { /*...*/ };

In particular, the names you choose don't have to match the name of the "original" template definition.


In the comments, you asked:

In the code from my question #1 and #3 have 2 and 1 parameter list respectively. Like why not include the other variables that was in the parameter list for the primary template?

This is how partial specialization works. But let's backup a bit.

This is a template class:

template<class T1, class T2>
struct X
{};

Here is an explicit specialization of it:

X<int, int>;

Now, let say I want to define a behaviour for X<T1, T2> with T1 == T2, this means not only X<int, int>, but also X<double, double>, X<float&, float&>, X<std::string, std::string>, and all the infinity of those explicit specializations.

To do so, I need to define a partial specialization which depends on one type (the one playing the roles of both T1 and T2). Thus, this specialization will be introduced by template<class T>: only one type:

template<class T>
struct X<T, T>
{ using type = T; };

If you had used two parameters (template<class T, class WhatToDoWithThatType>), what would be the use of the second one?



Related Topics



Leave a reply



Submit