Search code examples
c++templatesc++11traitsenable-if

Group class template specializations


Is there a technique / best style to group class template specializations for certain types ?

An example : Lets say I have a class template Foo and I need to have it specialized the same for the typeset

A = { Line, Ray }

and in another way for the typeset B

B = { Linestring, Curve }

What I'm doing so far : (the technique is also presented here for functions)

#include <iostream>
#include <type_traits>
using namespace std;

// 1st group
struct Line    {};
struct Ray     {};
// 2nd group 
struct Curve      {};
struct Linestring {};

template<typename T, typename Groupper=void>
struct Foo
{ enum { val = 0 }; };

// specialization for the 1st group 
template<typename T>
struct Foo<T, typename enable_if<
    is_same<T, Line>::value ||
    is_same<T, Ray>::value
>::type>
{
    enum { val = 1 };
};

// specialization for the 2nd group 
template<typename T>
struct Foo<T, typename enable_if<
    is_same<T, Curve>::value ||
    is_same<T, Linestring>::value
>::type>
{
    enum { val = 2 };
};

int main() 
{
    cout << Foo<Line>::val << endl;
    cout << Foo<Curve>::val << endl;
    return 0;
}

An extra helper struct enable_for would shorten the code (and allow to write the accepted types directly). Any other suggestions, corrections? Shouldn't this involve less effort?


Solution

  • You can also do this with your own traits and without enable_if:

    // Traits
    
    template <class T>
    struct group_number : std::integral_constant<int, 0> {};
    
    template <>
    struct group_number<Line> : std::integral_constant<int, 1> {};
    
    template <>
    struct group_number<Ray> : std::integral_constant<int, 1> {};
    
    template <>
    struct group_number<Linestring> : std::integral_constant<int, 2> {};
    
    template <>
    struct group_number<Curve> : std::integral_constant<int, 2> {};
    
    
    // Foo
    
    template <class T, int Group = group_number<T>::value>
    class Foo
    {
      //::: whatever
    };
    
    template <class T>
    class Foo<T, 1>
    {
      //::: whatever for group 1
    };
    
    template <class T>
    class Foo<T, 2>
    {
      //::: whatever for group 2
    };
    

    This has the advantage of automatically ensuring that each type is in at most one group.