Шаблонная специализация функций внутри шаблонного класса - PullRequest
4 голосов
/ 09 апреля 2009

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

Пример:

template< class Parm1, class Parm2, class Parm3 >
class Class {
public:
   void   Func( Parm1 arg1, Parm2 arg2 ) {
      Call<Parm3>( arg1, arg2 ); 
   }

protected:
   template< class Type >
   void   Call( Parm1 arg1, Parm2 arg2 ) {
   }

   template<>
   void   Call<void>( Parm1 arg1, Parm2 arg2 ) {
   }
};

Так что, если Parm3 имеет тип 'void', я хочу, чтобы был вызван второй Call. В противном случае первый. VS это работает хорошо, но GCC рвется по всему этому. Это всегда вызывает первый. Теперь это проблема со специализацией в неспециализированном классе или это связано с тем, что я специализируюсь на 'void'

Любая помощь была бы великолепна. Спасибо.

1 Ответ

7 голосов
/ 09 апреля 2009

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

Простой способ - использовать шаблон type2type вместе с перегрузкой:

template<typename T> struct t2t { typedef T type; };

void Func( Parm1 arg1, Parm2, arg2 ) { Call<Parm3>(arg1, arg2, t2t<Parm3>()); }
template< class Type, class V >  void Call( Parm1 arg1, Parm2 arg2, t2t<V>) { }
template< class Type >  void Call( Parm1 arg1, Parm2 arg2, t2t<void>) { }

Теперь он вызовет вторую Call перегрузку, если вы вызовете его с помощью t2t<void>, а первый - иначе, потому что первый менее особенный.

Возможно также использование enable_if:

void Func( Parm1 arg1, Parm2, arg2 ) { Call<Parm3>(arg1, arg2); }

template< class Type > typename disable_if< is_same<Type, void> >::type
Call( Parm1 arg1, Parm2 arg2) { }

template< class Type > typename enable_if< is_same<Type, void> >::type 
Call( Parm1 arg1, Parm2 arg2) { }

Теперь второй берется, если Type является недействительным, и первый берется, если Type снова является чем-то другим. Но с использованием другой техники. Этот называется SFINAE. Альтернативный способ, который снова добавляет один параметр, заключается в следующем: продемонстрировать, как работает SFINAE:

void Func( Parm1 arg1, Parm2, arg2 ) { Call<Parm3>(arg1, arg2); }

template< class Type >
void Call( Parm1 arg1, Parm2 arg2, char(*)[!is_same<Type, void>::value] = 0) { }

template< class Type > 
void Call( Parm1 arg1, Parm2 arg2, char(*)[ is_same<Type, void>::value] = 0) { }

SFINAE происходит, если замена параметра шаблона приводит к неверному типу или конструкции. Ниже мы пытаемся создать указатель на массив размером 0 или 1 соответственно. Массив размера 0 недопустим и вызовет сбой SFINAE - соответствующая специализация шаблона не будет считаться кандидатом на вызов, если она является функцией.

В приведенном выше случае enable_if он работает по-другому. Если enable_if дано что-то, полученное из false_type, то это делает его ::type typedef несуществующим. is_same происходит от false_type в случаях, когда типы дел не совпадают. Затем мы попытались бы получить доступ к несуществующему имени, которое является недопустимой конструкцией и, следовательно, также будет с ошибкой SFINAE.

...