Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 925
Nikos Athanasiou Avatar asked Jul 08 '14 09:07

Nikos Athanasiou


People also ask

What is a template specialization?

The act of creating a new definition of a function, class, or member of a class from a template declaration and one or more template arguments is called template instantiation. The definition created from a template instantiation is called a specialization.

Can we pass Nontype parameters to templates?

Template classes and functions can make use of another kind of template parameter known as a non-type parameter. A template non-type parameter is a template parameter where the type of the parameter is predefined and is substituted for a constexpr value passed in as an argument.

What is the syntax for explicit class specialization?

What is the syntax to use explicit class specialization? Explanation: The class specialization is creation of explicit specialization of a generic class. We have to use template<> constructor for this to work. It works in the same way as with explicit function specialization.

What are class templates explain the structure with example?

Class Template: We can define a template for a class. For example, a class template can be created for the array class that can accept the array of various types such as int array, float array or double array.


2 Answers

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.

like image 117
Angew is no longer proud of SO Avatar answered Oct 21 '22 11:10

Angew is no longer proud of SO


Extra level of indirection by using two new type traits:

template<class T>
struct is_from_group1: std::false_type {};

template<>
struct is_from_group1<Line>: std::true_type {};

template<>
struct is_from_group1<Ray>: std::true_type {};

template<class T>
struct is_from_group2: std::false_type {};

template<>
struct is_from_group2<Curve>: std::true_type {};

template<>
struct is_from_group2<Linestring>: std::true_type {};

and then do the enable_if on these type traits

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

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

Note that you still need to make sure that no user-defined class is added to both groups, or you will get an ambiguity. You can either use @Angew's solution to derive from a numbered group using std::integral_constant<int, N> for group number N. Or, if these groups are not logically exclusive, you could add an extra condition inside the enable_if that guards against this.

like image 45
TemplateRex Avatar answered Oct 21 '22 10:10

TemplateRex