Подтвердить что ты не робот

Отправка тегов против статических методов на частично специализированных классах

Предположим, что я хочу написать общую функцию 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);
}

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

4b9b3361

Ответ 1

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

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

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

Ответ 2

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);

Ответ 3

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

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

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

Ответ 4

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

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)
{
}