Специализировать шаблон участника без специализации его родителя

У меня есть шаблон класса, вложенный внутри другого шаблона. Частично специализировать это легко: я просто объявляю еще один блок template< … > внутри своего родителя.

Однако мне нужна другая частичная специализация, которая позволяет указать все его аргументы локального шаблона. Это превращает его в явную специализацию. Явные специализации по любой причине должны быть в области пространства имен. Чтобы объявить его за пределами своего родительского класса, родитель должен быть номинирован, для чего требуется непустой список аргументов шаблона. Это подразумевает частичную специализацию. Частичная специализация - это то, что я делаю, и она должна работать в произвольном внешнем масштабе. Но как GCC, так и Comeau не могут идентифицировать параметр шаблона в родительской номинации с формальными аргументами частичной специализации.

template< class X > struct A {
    template< class Y > struct B; // initial declaration OK

    template< class Z >
    struct B< A< Z > > {}; // partial OK as long as there a local arg

    template<> // ERROR: this syntax triggers explicit specialization
    struct B< int > {};
};

template<> // ERROR: can't nest template<>s here (why?)
template< class X > // ERROR: can't deduce X from type of A<X>::B<int> (why?)
struct A< X >::B< int > {};

(Я оставил весь свой нерабочий код, прокомментируйте его, чтобы попытаться понять).

Ответ 1

Это запрещено в стандарте С++ 14.7.3/18:

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

Ответ 2

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

Поэтому я предлагаю другое обходное решение:

namespace detail
{
  template <class X, class Z> class BImpl;
  template <class X, class Z> class BImpl<X, A<Z> > {};
  template <class X> class BImpl<X,int> {};
}

template <class X>
class A
{
  template <class Z> struct B: BImpl<X,Z> {};
};

Просто помните, что для передачи X требуется аргумент BImpl, если вы хотите также специализировать A. Смешно, что в этом случае я получаю только частичную специализацию!

Ответ 3

Сложный материал. Ваш начальный код ICE VC10 Beta2, хорошо.

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

template<> 
template< class X > 
struct A< X >::B< int > {};

X является параметром шаблона для структуры A, а B является полностью специализированным, поэтому я думаю, что это должно быть так:

template< class X > 
template<> 
struct A< X >::B< int > {};

Но даже это не скомпилируется. Текст ошибки действительно полезен, однако:

a.cpp a.cpp(11): ошибка C3212: "A:: B": явный специализация члена шаблона должен быть членом явного специализация         a.cpp(8): см. объявление "A:: B"

Похоже, что законно полностью специализировать B, если вы также полностью специализируете A.

Изменить: Хорошо, я слышал от кого-то, кто может говорить на это авторитетно, - перефразировать, это очень мрачная область в стандарте, и это открытая проблема с Комитетом С++ по очистке он ( "он" является явной специализацией членов шаблонов классов). В ближайшей перспективе совет: "Не делай этого".

Ответ 4

По крайней мере, это работает в VC 2010. Но я не могу написать def. fun() для "int" вне объявления класса. EDIT: К сожалению, g++ имеет также проблемы с компиляциями. EDIT: приведенный ниже код работал на VC 2010.

template<typename X>
class A
{
public:
    A()
    {

    }

    template<typename Y>
    struct B
    {
        void fun();
    };

    template<>
    struct B<int>
    {
        void fun()
        {
            cout << "Specialized version called\n";
        }
        //void fun();
    };



public:

    B<X> b;
};



template<typename X>
template<typename Y>
void A<X>::B<Y>::fun()
{
    cout << "templated version called\n";
}

int main()
{
   A<int> a;
    a.b.fun();
    A<float> a1;
    a1.b.fun();
}