Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Tag dispatch versus static methods on partially specialised classes

Suppose I want to write a generic function void f<T>(), which does one thing if T is a POD type and another thing if T is non-POD (or any other arbitrary predicate).

One way to achieve this would be to use a tag-dispatch pattern like the standard library does with iterator categories:

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

An alternative would be to use static member function of partially specialised types:

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

What are the pros and cons of using one method over the other? Which would you recommend?

like image 344
Peter Alexander Avatar asked Aug 02 '11 18:08

Peter Alexander


2 Answers

I would like tag dispatch because:

  • Easy to extend with new tags
  • Easy to use inheritance (example)
  • It is fairly common technique in generic programming

It seems tricky to me to add third variant in second example. When you'll want to add, for example non-POD-of-PODs type you'll have to replace bool in template <typename T, bool> struct f2; with something other (int if you like =) ) and replace all struct f2<T, bool-value> with struct f2<T, another-type-value>. So that for me the second variant looks hardly extensible. Please correct me if I wrong.

like image 97
tim Avatar answered Oct 31 '22 21:10

tim


A readable alternative to [boost|std]::enable_if, tags and partial specialization for simple compile-time dispatch that I like is the following:

[Remember that booleans have conversion to integers, that zero-length arrays are invalid and that offending templates are discarded (SFINAE). Also, char (*)[n] is a pointer to an array of n elements.]

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
}

It also has the advantage of not needing external classes which pollute the namespace. Now, if you want to externalize the predicate like in your question, you can do:

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
}

Usage:

foo<std::is_pod<T>::value>(some_variable);
like image 16
Alexandre C. Avatar answered Oct 31 '22 20:10

Alexandre C.