Есть ли способ специализировать шаблон функции по типу массива? - PullRequest
7 голосов
/ 16 апреля 2020

Допустим, нам нужен шаблон функции, который должен возвращать целое число в зависимости от типа:

template<typename T>
int get_type();

Далее, мы специализируем его на паре типов:

template<>
int get_type<int>()
{
  return TYPE_INT;
}

// And so on for other types...

И это хорошо работает, но не для типов массивов. Я могу сделать следующее:

template<>
int get_type<char[]>()
{
  return TYPE_STRING;
}

и компилятор "согласен" с этим, а компоновщик - нет. Поскольку тип char[] отличается, например, от char[5].

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

template<typename T>
int get_type(const T&);

Но на самом деле параметр функции здесь не нужен (не используется).

РЕДАКТИРОВАТЬ:

Я использую C ++ 11.

Ответы [ 4 ]

9 голосов
/ 16 апреля 2020

Вы не можете частично специализировать шаблонные функции (но вы можете для шаблонных классов)

Другой подход - диспетчеризация тегов с перегрузками вместо специализации:

template <typename> struct Tag{};

constexpr int get_type(Tag<int>) { return TYPE_INT; }

template <std::size_t N>
constexpr int get_type(Tag<char[N]>) { return TYPE_STRING; }

template <typename T>
constexpr int get_type() { return get_type(Tag<T>{}); }
8 голосов
/ 16 апреля 2020

Вам нужна частичная специализация для учета переменной длины массива, а C ++ не допускает частично специализированных шаблонов функций. Каноническое решение состоит в том, чтобы (частично) специализировать шаблон класса с (stati c) членом (функцией), и направить его в ваш шаблон неспециализированной функции:

namespace detail {
    template <typename T>
    struct get_type;

    template <>
    struct get_type<int> {
        static constexpr int value = TYPE_INT;
    };

    template <>
    struct get_type<char> {
        static constexpr int value = TYPE_CHAR;
    };

    template <typename T, std::size_t N>
    struct get_type<T[N]> {
        static constexpr int value = get_type<T>::value | TYPE_ARRAY;
    };

    template <std::size_t N>
    struct get_type<char[N]> {
        static constexpr int value = TYPE_STRING;
    };
} // namespace detail

template<typename T>
constexpr int get_type() {
    return detail::get_type<T>::value;
}
6 голосов
/ 16 апреля 2020

Вы не можете частично специализировать функцию для массива с размером. Но вы можете сделать это с помощью класса.

template<typename T>
class type
{
    static int get_type();
};

template<>
struct type<int>
{
    static int get_type() { return 1; }
};

template<size_t SZ>
struct type<char[SZ]>
{
    static int get_type() { return 2; }
};

template<typename T>
int get_type() { return type<T>::get_type(); }

int main()
{
    std::cout << get_type<char[3]>() << std::endl;
    return 0;
}

пример

3 голосов
/ 16 апреля 2020

Конрад уже описал лучший подход на мой взгляд.

Вот еще один подход с перегрузками и специализацией, основанный на SFINAE

// overload 1, for non-array types
template<typename T>
std::enable_if_t<!std::is_array_v<T>, int> get_type();

// specialization of overload 1 for int
template <>
auto get_type<int>() -> int {
    return 1;
}

// overload 2, for array types
template <typename T>
auto get_type() -> std::enable_if_t<std::is_array_v<T>, int> {
    return 3;
}

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...