частичная специализация шаблонов функций - PullRequest
8 голосов
/ 15 февраля 2011

В приведенном ниже фрагменте кода,

template<typename T1>
void func(T1& t)
{
    cout << "all" << endl;
}

template<typename T2>
void func(T2 &t)
{
    cout << "float" << endl;
}

// I do not want this
// template<> void func(float &t)

int main()
{
    int i; float f;
    func(i); // should print "all"
    func(f); // should print "float" 
    return 0;
}

Я хотел бы изменить шаблоны, которые при передаче любого типа, кроме float, будут печатать «all», а передача float - «float». Я не хочу шаблонную специализацию, вместо этого имею частичную специализацию, которая будет действовать соответственно на основе типа ввода. Как я должен идти об этом. Заранее спасибо.

Ну, сценарий, с которым я сейчас сталкиваюсь, похож, Мне нужно определить следующее,

template<typename T1>
void func(T1 &t)
{
    cout << "t1" << endl;
}

template<typename T2>
void func(T2 &t)
{
    cout << "t2" << endl;
}

Следующие вызовы должны вывести «t2»

func(int) // print "t2"
func(float) // print "t2"
func(string) // print "t2"

Следующие вызовы должны вывести «t1»

func(char) // print "t1"
func(xyz) // print "t1"
...
func(abc) // print "t1"

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

Ответы [ 5 ]

12 голосов
/ 15 февраля 2011

Вы можете комбинировать перегрузку функций с шаблонами. Итак:

#include <iostream>

template<typename T>
void func(T& t)
{
    std::cout << "all" << std::endl;
}

void func(float& f)
{
    std::cout << "float" << std::endl;
}

int main()
{
    int i; float f;
    func(i); // prints "all"
    func(f); // prints "float" 
    return 0;
}
6 голосов
/ 16 февраля 2011

Напишите класс характеристик типа для вашего состояния:

template<class T>
struct IsIntFloatOrString {
  enum { value = boost::is_same<T, int>::value
              or boost::is_same<T, float>::value
              or boost::is_same<T, string>::value };
};

Используйте boost :: enable_if и disable_if:

template<typename T1>
typename boost::enable_if<IsIntFloatOrString<T1> >::type
func(T1 &t) {
  cout << "t1" << endl;
}

template<typename T2>
typename boost::disable_if<IsIntFloatOrString<T2> >::type
func(T2 &t) {
  cout << "t2" << endl;
}
6 голосов
/ 15 февраля 2011

Вы не можете частично специализировать функции в C ++.

Возможно, это не та терминология, которую вы имеете в виду. Вы можете использовать шаблоны типа boost::is_same<T1, T2> для выполнения условной логики на основе заданного параметра шаблона. Вы также можете использовать T в любом месте, где бы вы использовали любой другой тип, например, в typeid(T).name():

template <typename T>
void foo(T&) {
   if (boost::is_same<T, int>::value)
      std::cout << "int lol";
   else
      std::cout << typeid(T).name();
}

(хотя я бы не рекомендовал использовать typeid (). Name (), так как его значение не указано стандартом и может варьироваться от типа, написанного в вашем коде, до искаженного символа или текста * * * *) Pokerface .)

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

3 голосов
/ 15 февраля 2011

Как уже говорил Томалак в своем ответе, вы не можете частично специализировать шаблонную функцию, но если вы измените свою функцию на статическую функцию-член в шаблонном классе, вы можете сделать это.

Однако лучшим подходом будет перегрузка функций.

0 голосов
/ 18 августа 2013

Это как заставить работать без уродливого синтаксиса a and !b and !c для enable_if в случае произвольного числа условий.

Если мы знаем, что частичная специализация не работает, а работает с классами, давайте использовать классы! Мы должны скрывать это от людей, но мы можем использовать их!

ОК, код:

#include <type_traits>
#include <iostream>


template <typename T>
class is_int_or_float : public std::integral_constant<bool, std::is_same<T, int>::value || std::is_same<T, float>::value> {
};


template<typename T, typename Enable = void> //(2)
struct Helper {
    static void go(const T&) {
                std::cout << "all"<< std::endl;
        }
};

template<typename T>
struct Helper<T, typename std::enable_if<is_int_or_float<T>::value>::type> { // (3)
        static void go(const T&) {
                std::cout << "int or float" << std::endl;
        }
};

template<typename T>
struct Helper<T, typename std::enable_if<std::is_pointer<T>::value>::type> { // (3)
        static void go(const T&) {
                std::cout << "pointer" << std::endl;
        }
};

template<typename T>
void func(const T& arg) {
        Helper<T>::go(arg); // (1)
}
int main() {
        char c;
        int i;
        float f; 
        int* p;
        func(c);
        func(i);
        func(f);
        func(p);
}

(1) Прежде всего только для каждого типа помощника по вызову. Нет специализации для функций.
(2) Здесь мы добавим один фиктивный аргумент. Нам не нужно указывать это при вызове, потому что по умолчанию это void (3) В 3 мы просто даем void, когда мы разрешаем T и все остальное (или SFINAE, как в нашем случае). Одна важная вещь заключается в том, что мы не должны допускать T дважды или более.

Примечания:

  1. Мы также можем изменить тип по умолчанию на std :: true_type, после этого мы сможем избавиться от std::enable_if (std::enable_if<some_trait<T>::value> изменится на some_trait<T>::type). Я не уверен, какой

  2. Этот код использует черты типа из C ++ 11. Если у вас нет поддержки c ++ 11, вы можете написать свои собственные черты или использовать черты типа от boost

Живой пример

...