Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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;
}
like image 389
Trevor Hickey Avatar asked Dec 20 '13 08:12

Trevor Hickey


People also ask

What are the conditions to overload a function?

If any class has multiple functions with different parameters having the same name, they are said to be overloaded. If we have to perform a single operation with different numbers or types of arguments, we need to overload the function.

Which function we Cannot overload?

2) Member function declarations with the same name and the name parameter-type-list cannot be overloaded if any of them is a static member function declaration.

Why we Cannot overload a function on return type?

During compilation, the function signature is checked. So, functions can be overloaded, if the signatures are not the same. The return type of a function has no effect on function overloading, therefore the same function signature with different return type will not be overloaded.

Can you overload methods in Rust?

Rust allows for a limited form of operator overloading. There are certain operators that are able to be overloaded. To support a particular operator between types, there's a specific trait that you can implement, which then overloads the operator.


2 Answers

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;
}
like image 71
Potatoswatter Avatar answered Sep 30 '22 04:09

Potatoswatter


I think using tag despatch would be easier than SFINAE.

template<class T>
struct Category;

template<>
struct Category<Type_A> : std::integral_constant<int, 1> {};
template<>
struct Category<Type_B> : std::integral_constant<int, 1> {};
template<>
struct Category<Type_C> : std::integral_constant<int, 1> {};

template<>
struct Category<Type_D> : std::integral_constant<int, 2> {};
template<>
struct Category<Type_E> : std::integral_constant<int, 2> {};
template<>
struct Category<Type_F> : std::integral_constant<int, 2> {};

template<class T>
void foo(std::integral_constant<int, 1>, T x)
{
    // Category 1 types.
}

template<class T>
void foo(std::integral_constant<int, 2>, T x)
{
    // Category 2 types.
}

template<class T>
void foo(T x)
{
    foo(Category<T>(), x);
}
like image 35
Simple Avatar answered Sep 30 '22 04:09

Simple