Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can C++ enable_if have a default implementation?

Tags:

c++

templates

I want to write a function print which behaves differently according to the type of its argument.

Here is my implementation:

template <typename T, typename std::enable_if<std::is_array<T>::value, int>::type = 0>
void print(const T &v) {
  std::cout << "array: ";
  for (const auto &e : v) {
    std::cout << e << ", ";
  }
  std::cout << std::endl;
}

template <typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
void print(const T &v) {
  std::cout << "integral: " << v << std::endl;
}

template <typename T, typename std::enable_if<!(std::is_array<T>::value || std::is_integral<T>::value), int>::type = 0>
void print(const T &v) {
  std::cout << "default: " << v << std::endl;
}

This code works as expected, but the conditions in the last specification are too complicated.

Is there any solution to simplify the last one?

like image 336
SaltyEgg Avatar asked Mar 19 '16 05:03

SaltyEgg


2 Answers

A general approach you can use for a default case is to have a function which takes a variable argument list. This will only be used if no other function matches. Here is an example:

template <typename T, typename std::enable_if<std::is_array<T>::value, int>::type = 0>
void print_helper(const T &v,int) {
  std::cout << "array: ";
  for (const auto &e : v) {
    std::cout << e << ", ";
  }
  std::cout << std::endl;
}

template <typename T, typename std::enable_if<std::is_integral<T>::value, int>::type = 0>
void print_helper(const T &v,int) {
  std::cout << "integral: " << v << std::endl;
}

template <typename T>
void print_helper(const T &v,...) {
  std::cout << "default: " << v << std::endl;
}

template <typename T>
void print(const T &v)
{
  print_helper(v,0);
}

For only two overloads, the extra function may not be worth it, but as you get more overloads this form can really pay off for the default case.

like image 113
Vaughn Cato Avatar answered Oct 04 '22 20:10

Vaughn Cato


We can use an extra chooser to make things better for us, courtesy of Xeo:

struct otherwise{ otherwise(...){} };

template<unsigned I>
struct choice : choice<I+1>{};

// terminate recursive inheritence at a convenient point,
// large enough to cover all cases
template<> struct choice<10>{};

Then each ranking on our choice list will be preferred to the next, and we just have to disable as we go:

// just forward to our first choice
template <class T> void print(const T &v) { print(v, choice<0>{}); }

Where our top choice is array:

template <class T, class = std::enable_if_t<std::is_array<T>::value>>
void print(const T& v, choice<0> ) { ... }

And then integral:

template <class T, class = std::enable_if_t<std::is_integral<T>::value>>
void print(const T& v, choice<1> ) { ... }

And then anything

template <class T>
void print(const T& v, otherwise ) { ... }

This structure allows for arbitrarily many choices.

like image 45
Barry Avatar answered Oct 04 '22 21:10

Barry