Этот вопрос связан с предыдущий вопрос& в котором упоминался отчет об ошибке для gcc (предположительно исправленный в gcc 4.5.0) и касающийся некоторых особенностей частичной специализации шаблона вложенного класса.
Моя установка в том, что у меня есть класс Base
с вложенным шаблоном класса Inner
это частично специализируется на char
(используя трюк с фиктивным параметром, потому что явная спецификация не разрешена в классе).
#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>
был частично специализирован?
Онлайн пример использования gcc 4.8.0. Я специально ищу цитаты из Стандарта, которые объясняют это поведение.
Частичная специализация должна переопределить то же имя, что и основной шаблон, для которого оно предоставляет альтернативное определение.
Когда ты пишешь struct Inner
в рамках Derived
объявляешь Derived::Inner
, Base::Inner
отличное имя от Derived::Inner
и поэтому объявляет другой класс. Не возможно специализироваться Base::Inner
с декларацией, которая объявляет Derived::Inner
,
Когда ты пишешь Derived::Inner
в области имен пространства поиск имени разрешает это имя в Base::Inner
— все специализации одного и того же класса: Base::Inner
, даже если вы относитесь к ним как Derived::Inner
,
Из стандарта:
[Temp.class.spec]Частичная специализация шаблона класса обеспечивает альтернативное определение шаблона, который используется вместо основного определения, когда аргументы в специализации совпадают с аргументами, указанными в частичной специализации.
Специализированные шаблоны не являются частью полиморфизма.
Вы на самом деле объявляете тип. Таким образом, любой модуль компиляции, который может видеть производный заголовочный файл с реализацией для специализации шаблона, будет использовать эту специализацию для вложенного класса шаблона.
Компилятор пытается найти класс наилучшего соответствия и всегда будет выбирать специализированный тип по умолчанию. Так что даже если вы попытаетесь получить доступ к области действия базового типа, это все тот же класс.
То же самое произошло бы, если бы вы специализировали класс шаблона в любой другой части вашего кода. Компилятор выберет наиболее подходящую специализацию, если ее нет, он примет «значение по умолчанию».