2010-03-29 2 views
39
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 

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

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

К сожалению, никто на comp.std.C++ не осмелился ответить, поэтому я снова помещаю его сюда с щедростью.

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

+1

Что же касается, почему я предлагаю просить здесь вместо: HTTP : //groups.google.com/group/comp.std.c++ –

+0

Спасибо за предложение. – hirschhornsalz

ответ

25

Мое предположение относительно того, почему это происходит: полная специализаций больше не «шаблонных классов/функций», они являются «настоящими» классы/методы, и получить, чтобы иметь реальное (линкера). Но для полностью специализированного шаблона внутри частично специализированного это было бы неверно. Возможно, это решение было принято только для того, чтобы упростить жизнь составителей-компиляторов (и сделать жизнь сложнее для кодировщиков, в процессе: P).

+0

Это, пожалуй, хорошая причина. Но OTOH, не могли ли авторы компилятора сделать то же самое, что и я, добавляя невидимый неиспользованный параметр фиктивного? – hirschhornsalz

+9

Вот почему я делаю анекдот в круглых скобках. Понятно, что нет такой тяжелой причины, как «она никогда не сможет работать, потому что [...]», поэтому лучшей возможной причиной отказа было «не путать пользователей». Но это было бы смешно, учитывая, как многие другие способы программирования программиста на n00b C++ могут запутаться :). Я на самом деле обнаружил, что C++ отлично справляется с запутыванием ничего не подозревающего новичка и не позволяет очень продвинутому программисту выполнять действительную (а иногда и элегантную) работу, для всех, по-видимому, крошечных деталей реализации. Но это только мое мнение, конечно :) – Virgil

+0

Чтобы быть справедливым, есть и другие последствия, которые вы не учли. Скажем, вы определяете A так же, как и вы, а компилятор добавляет дополнительный параметр фиктивного состояния. Но поскольку это шаблон, он включается в несколько файлов .cpp (но не ВСЕ из них!). Скажем, вы попадаете в ситуацию, когда в файлах x.cc & y.cc вы используете версию A, для которой нужен фиктивный параметр; в то время как в файле z.cc вы в конечном итоге используете совершенно другую версию A, которая является шаблоном, но имеет два аргумента типа от начала (и не требует манекена). Как вы определяете разницу во времени соединения? – Virgil

8

Стандарт C++ явно запрещает полную специализацию классов шаблонов членов в первом случае. Согласно 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.

+7

Это не ответ на мой вопрос. Если бы вы прочитали мой вопрос, вы бы отметили, что я уже знаю, что явная специализация вложенного класса не допускается. И я уже нашел способ обойти этот запрет. Мой вопрос был __Почему это так? __ – hirschhornsalz

+2

Только создатели C++ Standard действительно знают, почему это так. –

+0

Возможно :-) Но я надеялся, что читатель SO тоже об этом знает :-) – hirschhornsalz

4

Резервное копирование аргумент Вергилия (он был быстрее, чем я проводкой же обоснование), считают это:

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

Является Тиннер полностью специализированы, или частично специализированные из-T1?

Edit:

После рассмотрения некоторых других замечаний - кажется, что вы хотите иметь полную специализацию на основе параметра шаблона во внешнем классе. Если вы вкладываете реализация внутреннего класса, в том, что, кажется, работает в 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 :: Тиннер правильно будет специализация Тиннер. Я не смог его скомпилировать с реализацией вне шаблона.

+0

Я бы сказал, что TOuter :: TInner частично специализирован, в то время как TInner полностью специализирован, но неполный тип ;-) – hirschhornsalz

+0

И предполагая, что вы делаете «семантическую ошибку» (или просто ссылаетесь на несуществующий символ) в TOuter :: TInner - вы должны получить ошибку компиляции/компоновщика или нет? ;) (считайте, что TOuter никогда не может быть использован ...) – Virgil

+0

У меня был именно этот случай в реальном коде - ошибочно типизированный конструктор в специализированном внутреннем классе. (Я использую обход частичной специализации, описанный в начале.) Ошибка появляется только при объявлении переменной типа TOuter. – hirschhornsalz

7

Вы можете обойти это поведение, делегируя реальную работу в другую структуру, хотя:

namespace detail 
{ 
    template <class T, class U> 
    struct InnerImpl {}; 
} 

template <class T> 
struct Outer 
{ 
    template <class U> 
    struct Inner: detail::InnerImpl<T,U> 
    { 
    }; 
}; 

Теперь вы можете специализироваться InnerImpl как вы хотите

+0

Интересная идея. Но будет ли это работать для меня, потому что мне нужна специализация внутреннего класса, зависящая от параметра шаблона внешнего класса? – hirschhornsalz

+2

Да: как вы можете заметить, я передаю 'T' в класс шаблонов InnerImpl. –

+0

Интересно, хотя если это лучшее решение, чем использование аргумента шаблона шаблона. –

Смежные вопросы