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?
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With