Understanding partial specialization of inherited

2019-07-25 06:11发布

问题:

This question is connected to a previous Q&A in which a bug report for gcc was mentioned (supposedly fixed in gcc 4.5.0) and concerns some peculiarities for partial specialization of nested class template.

My setup is that I have a class Base with a nested class template Inner that is partially specialized for char (using the dummy parameter trick, because explicit speciliaztion is not allowed in-class).

#include <type_traits>
#include <iostream>
#include <ios>

struct Base
{
    // dummy template parameter...
    template<class U, class _ = void> struct Inner: std::true_type {};

    // ... to allow in-class partial specialization
    template<class _> struct Inner<char, _>: std::false_type {};
};

I now define a Derived class for which I further want to specialize Inner, which for some odd reason cannot be done in-class (even though it is still a partial specialization).

struct Derived
:
    Base
{
    // cannot partially specialize Inner inside Derived...
    //template<class _>
    //struct Inner<int, _>: std::false_type {};
};

// ... but specializing Derived::Inner at namespace scope, also specializes it for Base::Inner
template<class _> struct Derived::Inner<int, _>: std::false_type {};

First question: why do I have to partially specialize Derived::Inner at namespace scope?

But the strangest part is that when I call the various partial specializations of Inner from both Base and Derived, the partial specialization for int that I only did for Derived, also applies to Base.

int main()
{
    std::cout << std::boolalpha << Base::Inner<float>::value << "\n";    
    std::cout << std::boolalpha << Derived::Inner<float>::value << "\n";    

    std::cout << std::boolalpha << Base::Inner<char>::value << "\n";    
    std::cout << std::boolalpha << Derived::Inner<char>::value << "\n";    

    std::cout << std::boolalpha << Base::Inner<int>::value << "\n";      // huh???
    std::cout << std::boolalpha << Derived::Inner<int>::value << "\n";   // OK 
}

Second question: why is Base::Inner<int>::value equal to false, even though only Derived::Inner<int> was partially specialized?

Online example using gcc 4.8.0. I am specifically looking for quotes from the Standard that explain this behavior.

回答1:

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.



回答2:

Specializing templates isn't a part of polymorhpism.

You're in fact declaring a type. So any compilation unit that can see the derived header file with the implementation for the template specialization will use that specialization for the nested template class.

The compiler tries to find the best match class, and will always choose the specialized type over the default. So even if you try to access the scope of the base type it's still the same class.

Same thing would happen if you specialize a template class in any other part of your code. The compiler will choose the best matching specialization, if there is non, it will take the "default".