1

Этот вопрос связан с a previous Q&A, в котором упоминается отчет об ошибке для gcc (предположительно фиксированный в gcc 4.5.0) и касается некоторых особенностей частичной специализации шаблона вложенного класса.Понятие частичной специализации унаследованных вложенных шаблонов классов

Моей установкой является то, что у меня есть класс Base с шаблоном вложенного класса Inner, который частично специализирован для char (с использованием фиктивного параметра трюка, так как явное speciliaztion не допускаются в классе).

#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 {}; 
}; 

я теперь определить Derived класс, для которого я также хочу специализироваться Inner, который по какой-то причине не может быть сделано в классе (даже если он по-прежнему частичная специализация).

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 {}; 

Первый вопрос: почему я должен частично специализировать Derived::Inner в области видимости пространства имен?

Но самое странное в том, что, когда я называю различные частичные специализации Inner как из Base и Derived, частичной специализации int, что я сделал только для Derived, также относится и к 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 
} 

Второй вопрос: почему Base::Inner<int>::value равно false, хотя только Derived::Inner<int> был частично специализированный?

Online example using gcc 4.8.0. Я специально ищу цитаты из Стандарта, которые объясняют это поведение.

ответ

1

Частичная специализация должна переопределять то же имя, что и основной шаблон, для которого он предоставляет альтернативное определение.

Когда вы пишете struct Inner в рамках Derived, вы объявляете Derived::Inner. Base::Inner - это отличное имя от Derived::Inner и поэтому объявляет другой класс. Невозможно специализировать Base::Inner с декларацией, объявляющей Derived::Inner.

Когда вы пишете Derived::Inner в области видимости пространства имен, имя поиск решает, что имя Base::Inner - специализации все тот же класс: Base::Inner, даже если вы обратитесь к ним как Derived::Inner.

От стандарта:

[temp.class.spec]

Частичная специализация шаблона класса обеспечивает альтернативное определение шаблона, который используется вместо основного определения при аргументы в специализации соответствуют тем, которые указаны в частичной специализации.

+0

tnx для ответа + стандартная цитата! – TemplateRex

+0

мое удовольствие! Я все еще не уверен, разрешаю ли объявление «Derived :: Inner» вне класса легально или ошибка компилятора. – willj

1

Специализированные шаблоны не являются частью полиморпизма.

Вы действительно объявляете тип. Таким образом, любая единица компиляции, которая может видеть полученный файл заголовка с реализацией для специализации шаблона, будет использовать эту специализацию для вложенного шаблона.

Компилятор пытается найти лучший класс соответствия и всегда будет выбирать специализированный тип по умолчанию. Поэтому, даже если вы попытаетесь получить доступ к области базового типа, это все тот же класс.

То же самое произойдет, если вы специализируетесь на классе шаблонов в любой другой части вашего кода. Компилятор выберет наилучшую совпадающую специализацию, если нет, он примет значение «по умолчанию».

+0

спасибо за этот ответ на мой второй вопрос. Является ли это также причиной того, что я не могу частично специализировать 'Inner' внутри' Derived', хотя я могу сделать это внутри 'Base'? – TemplateRex

+0

@TemplateRex Поскольку класс не входит в область производного. Он находится в области Base. Точно так же, как если бы вы попытались создать реализацию Base :: SomeFunction() в производном. (без переопределения или перегрузки. фактическая реализация). Объем реализации находится под базой :: ... поэтому, если вы попытаетесь это сделать в области класса Derived, фактическая область, к которой вы будете обращаться, - это: Derived :: Base :: –