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


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

ні, це цілком нормально, це правило застосовується лише до шаблонів класів (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>) {

   }
};

навіщо потрібна type<>обгортка? не міг привід 0 до покажчика типу Tзробити фокус? Думаю, це не так елегантно ...
Нім,

Здається, цього насправді неможливо зробити. Дякую.
ledokol

3
@Nim правильно, я вважаю, що вказівник приведення є потворним, і не буде працювати для типів, на які ви не можете сформувати вказівники (посилання). Крім того, наявність параметра функції як вказівник на тип масиву без розміру заборонено в C ++. Маючи його в обгортці типу, це працює для всіх типів.
Йоханнес Шауб - літб,

2
@ JohannesSchaub-litb: Якими є інші варіанти перевантаження? Може показати деякі з них?
Жан-Бернар Янсен

2
@ fast-reflexes його коментар полягав у використанні template<typename T> void get_as(T*); void get_as(double*);та передачі a (T*)0.
Йоханнес Шауб - Litb

24

Якщо хтось може використовувати, std::enable_ifми могли б покластися на SFINAE (невдача заміни не є помилкою)

це могло б працювати так (див. Прямий ефір ):

#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.


Оновив публікацію, щоб зробити її більш сучасною.
Габріель
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.