явная специализация функции-члена класса шаблона

88

Мне нужно специализировать функцию-член шаблона для некоторого типа (скажем, двойного ). Он отлично работает, пока Xсам класс не является классом-шаблоном, но когда я его делаю, GCC начинает выдавать ошибки времени компиляции.

#include <iostream>
#include <cmath>

template <class C> class X
{
public:
   template <class T> void get_as();
};

template <class C>
void X<C>::get_as<double>()
{

}

int main()
{
   X<int> x;
   x.get_as();
}

вот сообщение об ошибке

source.cpp:11:27: error: template-id
  'get_as<double>' in declaration of primary template
source.cpp:11:6: error: prototype for
  'void X<C>::get_as()' does not match any in class 'X<C>'
source.cpp:7:35: error: candidate is:
  template<class C> template<class T> void X::get_as()

Как я могу это исправить и в чем проблема?

Заранее спасибо.

ледокол
источник
2
это незаконно в текущем стандарте, чтобы специализироваться, вы также должны специализировать класс ...
Ним
но он работает, если класс не является шаблоном. Это тоже незаконно?
ledokol 01
нет, это прекрасно, это правило применяется только к шаблонам классов (AFAIK).
Ним

Ответы:

108

Так не работает. Вы должны были бы сказать следующее, но это не правильно

template <class C> template<>
void X<C>::get_as<double>()
{

}

Явно специализированные члены нуждаются в том, чтобы их окружающие шаблоны классов также были явно специализированы. Итак, вам нужно сказать следующее, что будет специализироваться только на этом участнике X<int>.

template <> template<>
void X<int>::get_as<double>()
{

}

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

template <class C> class X
{
   template<typename T> struct type { };

public:
   template <class T> void get_as() {
     get_as(type<T>());
   }

private:
   template<typename T> void get_as(type<T>) {

   }

   void get_as(type<double>) {

   }
};
Йоханнес Шауб - litb
источник
зачем тебе type<>обертка? неужели приведение 0 к указателю типа Tне поможет? Думаю, это не так элегантно ...
Ним
Похоже, это действительно невозможно. Спасибо.
ledokol 01
3
@ Ним, верно, я считаю, что приведение указателя некрасиво и не будет работать для типов, на которые вы не можете формировать указатели (ссылки). Кроме того, использование параметра функции в качестве указателя на тип массива без размера недопустимо в C ++. Наличие его в оболочке типа позволяет ему работать для всех типов.
Йоханнес Шауб - литб, 01
2
@ JohannesSchaub-litb: Какие еще варианты могут быть при перегрузках? Можете показать некоторые из них?
Жан-Бернар Жансен
2
@ fast-reflexes его комментарий состоял в том, чтобы использовать template<typename T> void get_as(T*); void get_as(double*);и передать (T*)0.
Йоханнес Шауб - лит
24

Если есть возможность использовать, std::enable_ifмы можем положиться на SFINAE (отказ замены не является ошибкой)

это будет работать так (см. LIVE ):

#include <iostream>
#include <type_traits>

template <typename C> class X
{
public:
    template <typename T, 
              std::enable_if_t<!std::is_same_v<double,T>, int> = 0> 
    void get_as() { std::cout << "get as T" << std::endl; }

    template <typename T, 
              std::enable_if_t<std::is_same_v<double,T>, int> = 0> 
    void get_as() { std::cout << "get as double" << std::endl; }
};

int main() {
   X<int> d;
   d.get_as<double>();

   return 0;
}

Ужасно то, что со всеми этими enable_if для компилятора должна быть доступна только одна специализация, иначе возникнет ошибка разрешения неоднозначности. Вот почему поведение по умолчанию «получить как T» также требует включения if.

Габриэль
источник
Обновил пост, чтобы сделать его более современным.
Габриэль