Search code examples
c++templatesc++11template-meta-programmingtype-traits

Can I overload functions with type-traits?


Let's say, I have six types, and they each belong in a conceptual category.
Here is a diagram that shows this:

Types A, B, and C wrapped inside a box called "Type Category 1" and types D, E, and F wrapped inside a box called "Type Category 2"


Or Perhaps a more specific example for you: Apple, Orange and Banana are all Fruit.  Carrot, Onion, and Cabbage are all Vegetables


I want to write two functions that will handle all 6 types.
Types in "Category 1" get handled a certain way, and types in "Category 2" get handled a different way.

Let's get into the code. First, I'll create the six types.

//Category 1 Types
class Type_A{};
class Type_B{};
class Type_C{};

//Category 2 Types
class Type_D{};
class Type_E{};
class Type_F{};

Next, I'll create two type traits so that the category of the type can be discovered at compile time.

/* Build The Category 1 Type Trait */

//Type_A Type Trait
template <typename T>
struct Is_Type_A {
  static const bool value = false;
};
template <>
struct Is_Type_A<Type_A> {
  static const bool value = true;
};

//Type_B Type Trait
template <typename T>
struct Is_Type_B {
  static const bool value = false;
};
template <>
struct Is_Type_B<Type_B> {
  static const bool value = true;
};

//Type_C Type Trait
template <typename T>
struct Is_Type_C {
  static const bool value = false;
};
template <>
struct Is_Type_C<Type_C> {
  static const bool value = true;
};

//Category 1 Type Trait
template <typename T>
struct Is_Type_From_Category_1 {
  static const bool value = Is_Type_A<T>::value || Is_Type_B<T>::value || Is_Type_C<T>::value;
};

/* Build The Category 2 Type Trait */

//Type_D Type Trait
template <typename T>
struct Is_Type_D {
  static const bool value = false;
};
template <>
struct Is_Type_D<Type_D> {
  static const bool value = true;
};

//Type_E Type Trait
template <typename T>
struct Is_Type_E {
  static const bool value = false;
};
template <>
struct Is_Type_E<Type_E> {
  static const bool value = true;
};

//Type_F Type Trait
template <typename T>
struct Is_Type_F {
  static const bool value = false;
};
template <>
struct Is_Type_F<Type_F> {
  static const bool value = true;
};

//Category 1 Type Trait
template <typename T>
struct Is_Type_From_Category_2 {
  static const bool value = Is_Type_D<T>::value || Is_Type_E<T>::value || Is_Type_F<T>::value;
};

Now that I have two type traits to distinguish what category each of the six types fall into, I want to write two functions. One function will accept everything from Category 1, and the other function will accept everything from Category 2. Is there a way to do this without creating some kind of dispatching function? Can I find a way to have only two functions; one for each category?


EDIT: I have tried to use enable_if like this, but such an attempt will result in a compiler error.

//Handle all types from Category 1
template<class T ,class = typename std::enable_if<Is_Type_From_Category_1<T>::value>::type >
void function(T t){
    //do category 1 stuff to the type
    return;
}

//Handle all types from Category 2
template<class T ,class = typename std::enable_if<Is_Type_From_Category_2<T>::value>::type >
void function(T t){
    //do category 2 stuff to the type
    return;
}

Edit 2: I've tried the code provided in the link, but this isn't a yes or no decision on whether or not to call the function. It's which function do I call, given two type traits. This would be a redefinition error.

//Handle all types from Category 2
template<class T, class dummy = typename std::enable_if< Is_Type_From_Category_1<T>::value, void>::type>
void function(T t){
    //do category 1 stuff to the type
    return;
}
//Handle all types from Category 2
template<class T, class dummy = typename std::enable_if< Is_Type_From_Category_2<T>::value, void>::type>
void function(T t){
    //do category 2 stuff to the type
    return;
}

Solution

  • Two functions signatures are not allowed to differ only by the default value of a template parameter. What would happen if you explicitly called function< int, void >?

    Usual usage of enable_if is as the function return type.

    //Handle all types from Category 1
    template<class T >
    typename std::enable_if<Is_Type_From_Category_1<T>::value>::type
    function(T t){
        //do category 1 stuff to the type
        return;
    }
    
    //Handle all types from Category 2
    template<class T >
    typename std::enable_if<Is_Type_From_Category_2<T>::value>::type
    function(T t){
        //do category 2 stuff to the type
        return;
    }