Диспетчеризация тегов и статические методы в частично специализированных классах - PullRequest
45 голосов
/ 02 августа 2011

Предположим, я хочу написать обобщенную функцию void f<T>(), которая делает одну вещь, если T является типом POD, и другую вещь, если T не является POD (или любым другим произвольным предикатом).

Одним из способов достижения этого было бы использование шаблона отправки тегов, как это делает стандартная библиотека с категориями итераторов:

template <bool> struct podness {};
typedef podness<true> pod_tag;
typedef podness<false> non_pod_tag;

template <typename T> void f2(T, pod_tag) { /* POD */ }
template <typename T> void f2(T, non_pod_tag) { /* non-POD */ }

template <typename T>
void f(T x)
{
    // Dispatch to f2 based on tag.
    f2(x, podness<std::is_pod<T>::value>());
}

Альтернативой может быть использование статической функции-члена частично специализированных типов:

template <typename T, bool> struct f2;

template <typename T>
struct f2<T, true> { static void f(T) { /* POD */ } };

template <typename T>
struct f2<T, false> { static void f(T) { /* non-POD */ } };

template <typename T>
void f(T x)
{
    // Select the correct partially specialised type.
    f2<T, std::is_pod<T>::value>::f(x);
}

Каковы плюсы и минусы использования одного метода над другим? Что бы вы порекомендовали?

Ответы [ 4 ]

15 голосов
/ 03 августа 2011

Я хотел бы отправить тег, потому что:

  • Легко расширить новыми тегами
  • Простое в использовании наследование ( пример )
  • Это довольно распространенная техника в обобщенном программировании

Мне кажется сложным добавить третий вариант во втором примере.Когда вы захотите добавить, например, тип не POD-of-POD, вам придется заменить bool в template <typename T, bool> struct f2; чем-то другим (int, если хотите =)) и заменить все struct f2<T, bool-value>с struct f2<T, another-type-value>.Так что для меня второй вариант выглядит едва растяжимым.Пожалуйста, поправьте меня, если я ошибаюсь.

15 голосов
/ 02 августа 2011

A для чтения альтернатива [boost|std]::enable_if, теги и частичная специализация для простой отправки во время компиляции, которая мне нравится, следующая:

[Помните, что логические значения имеют преобразование в целые числа, что массивы нулевой длины недопустимы, а шаблоны с ошибками отбрасываются (SFINAE). Также char (*)[n] является указателем на массив n элементов.]

template <typename T> 
void foo(T, char (*)[is_pod<T>::value] = 0)
{
    // POD
}

template <typename T> 
void foo(T, char (*)[!is_pod<T>::value] = 0)
{
    // Non POD
}

Он также имеет то преимущество, что не нуждается во внешних классах, которые загрязняют пространство имен. Теперь, если вы хотите экстернализовать предикат, как в вашем вопросе, вы можете сделать:

template <bool what, typename T>
void foo(T, char (*)[what] = 0)
{
    // taken when what is true
}

template <bool what, typename T>
void foo(T, char (*)[!what] = 0)
{
    // taken when what is false
}

Использование:

foo<std::is_pod<T>::value>(some_variable);
10 голосов
/ 07 октября 2013

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

Баренд, основной автор Boost.Geometry, объясняет оба метода и предпочитает последний. Это широко используется в Boost.Geometry. Вот краткие преимущества:

  • Нет необходимости создавать экземпляр тега, поскольку его единственная цель - дифференцировать
  • Легко определить новые типы и константы на основе тегов
  • Аргументы могут быть изменены в интерфейсе, то есть, скажем, distance(point, polygon); и distance(polygon, point); могут иметь только одну реализацию
1 голос
/ 21 октября 2015

Я знаю, что это старый вопрос с уже принятым ответом, но это может быть жизнеспособной альтернативой:

template<typename T>
std::enable_if_t<std::is_pod<T>::value> f(T pod)
{
}

template<typename T>
std::enable_if_t<!std::is_pod<T>::value> f(T non_pod)
{
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...