Специализируйте шаблон с помощью шаблона

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

template <typename T>
T get();

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

template <>
template <typename T>
foo_type<T> get<foo_type<T>>()

person cooky451    schedule 03.03.2012    source источник


Ответы (2)


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

Итак, решение таково:

namespace details
{
     template <typename T>
     struct worker
     {
         static T get();
     };

     template <typename T> //partial specialization of class is allowed
     struct worker<foo<T>>
     {
         static foo<T> get();
     };

}

template <typename T>
T get()
{
  return details::worker<T>::get();
}

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

namespace details
{
     template <typename T>
     static T get(T*); 

     template <typename T> 
     static foo<T> get(foo<T>*); //now the overload is valid

}

template <typename T>
T get()
{
  return details::get<T>(static_cast<T*>(0));
}

Обратите внимание, что аргумент static_cast<T*>(0) используется, чтобы помочь компилятору выбрать правильную перегрузку. Если T отличается от foo<U>, то будет выбрана первая перегрузка, поскольку тип переданного ей аргумента будет T*, а не foo<U>*. Если T равно foo<U>, компилятор выберет вторую перегрузку, поскольку она более специализирована и может принимать переданный ей аргумент, который в данном случае равен foo<U>*.

person Nawaz    schedule 03.03.2012
comment
Теперь, увидев решение, оно до боли очевидно. Благодарю вас! Но теперь я получаю ошибку компоновщика... есть идеи? - person cooky451; 03.03.2012
comment
@Xeo: Ой. Да. Ты прав. Когда я выложил это, я чувствовал, что делаю что-то не так, но не мог этого понять. - person Nawaz; 03.03.2012
comment
@cooky: Вы поместили тело функции шаблона в .cpp? :) - person Xeo; 03.03.2012
comment
Нет, ошибка связывания возникла при простой перегрузке функции. Довольно забавно, не спрашивайте меня, что там сделал компилятор. (Не очень очевидно, он просто доверил компоновщику свои задачи;)) - Однако теперь это работает, спасибо вам обоим, теперь я должен решить, что принять в качестве ответа. Может быть, я бросаю кости. ;) - person cooky451; 03.03.2012
comment
static_cast‹T*›(nullptr) - person Janek Olszak; 19.08.2014

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

template<class T>
struct get_impl{
  static T get(){ ... }
};

template<class T>
struct get_impl<foo_type<T> >{
  static foo_type<T> get(){ ... }
};

template<class T>
T get(){ return get_impl<T>::get(); }
person Xeo    schedule 03.03.2012