Как я могу написать шаблон функции для всех типов с определенной чертой типа?

Рассмотрим следующий пример:

struct Scanner
{
    template <typename T>
    T get();
};

template <>
string Scanner::get()
{
    return string("string");
}

template <>
int Scanner::get()
{
    return 10;
}

int main()
{
    Scanner scanner;
    string s = scanner.get<string>();
    int i = scanner.get<int>();
}

Класс Scanner используется для извлечения токенов из какого-либо источника. Приведенный выше код работает нормально, но терпит неудачу, когда я пытаюсь get использовать другие целочисленные типы, такие как char или unsigned int. Код для чтения этих типов точно такой же, как код для чтения int. Я мог бы просто продублировать код для всех других интегральных типов, которые я хотел бы прочитать, но я бы предпочел определить один шаблон функции для всех целочисленных типов.

Я пробовал следующее:

struct Scanner
{
    template <typename T>
    typename enable_if<boost::is_integral<T>, T>::type get();
};

Это работает как шарм, но я не знаю, как заставить Scanner::get<string>() снова работать. Итак, как мне написать код, чтобы я мог выполнять scanner.get<string>() и scanner.get<any integral type>() и иметь одно определение для чтения всех целочисленных типов?

Обновление: бонусный вопрос: что, если я захочу принять более одного диапазона классов на основе некоторых черт? Например: как мне подойти к этой проблеме, если я хочу иметь три функции get, которые принимают (i) целочисленные типы (ii) типы с плавающей запятой (iii) строки соответственно.


person TC.    schedule 29.03.2010    source источник


Ответы (2)


struct Scanner
{
    template <typename T>
    typename boost::enable_if<boost::is_integral<T>, T>::type get()
    {
        return 10;
    }
    template <typename T>
    typename boost::disable_if<boost::is_integral<T>, std::string>::type get()
    {
        return "string";
    }
};

Обновление "Что делать, если я хочу принять более одного диапазона классов на основе некоторых характеристик?"

struct Scanner
{
    template <typename T>
    typename boost::enable_if<boost::is_integral<T>, T>::type get()
    {
        return 10;
    }

    template <typename T>
    typename boost::enable_if<boost::is_floating_point<T>, T>::type get()
    {
        return 11.5;
    }

    template <typename T>
    std::string get(
          typename boost::disable_if<boost::is_floating_point<T>, T>::type* = 0, 
          typename boost::disable_if<boost::is_integral<T>, T>::type* = 0)

    {
        return std::string("string");
    }
};
person Community    schedule 29.03.2010
comment
Я хотел бы заметить, что вы, вероятно, могли бы использовать boost::mpl::and_ и boost::mpl::or_ для объединения аргументов в disable_if. +1 тем не менее :) - person Matthieu M.; 29.03.2010
comment
Вы также можете использовать ice_and и ice_or из библиотеки Boost. - person James McNellis; 29.03.2010

Отложить до другого шаблона. Вот общий шаблон для того, что вы хотите:

template <typename T, bool HasTrait = false>
struct scanner_impl;

template <typename T>
struct scanner_impl
{
    // Implement as though the trait is false
};

template <typename T>
struct scanner_impl<true>
{
    // Implement as though the trait is true
};

// This is the one the user uses
template <typename T>
struct scanner : scanner_impl<T, typename has_my_trait<T>::value>
{
};
person Kaz Dragon    schedule 29.03.2010
comment
Хорошая идея добавить косвенность таким образом. Возможно, это станет еще более гибким, если вы будете использовать не bool, а enum в качестве переключателя черт. - person xtofl; 29.03.2010
comment
@xtofl: Ага. Это также ответит на бонусный вопрос. - person Kaz Dragon; 29.03.2010