EFanZh EFanZh - 1 month ago 5
C++ Question

Simplify code for dispatching different types to different functions in C++

I want to write a function that prints value differently for iterator types and other types. After doing some research, here is what I came up with:

#include <iostream>
#include <iterator>
#include <experimental/type_traits>

namespace details
{
template <class T, class = void>
struct is_iterator : std::false_type
{
};

template <class T>
struct is_iterator<T, std::experimental::void_t<typename std::iterator_traits<T>::value_type>> : std::true_type
{
};

template <class T>
constexpr auto is_iterator_v = is_iterator<T>::value;
}

template <class T>
std::enable_if_t<!details::is_iterator_v<T>> print(const T &value)
{
std::cout << value;
}

template <class T>
std::enable_if_t<details::is_iterator_v<T>> print(const T &value)
{
std::cout << "(IteratorTo ";
print(*value);
std::cout << ')';
}


But I think my code is a little too long and a little complicated. Is it possible to make my code shorter and cleaner?

Answer

SFINAE is your friend here:

template <class T>
constexpr std::true_type is_iterator(typename std::iterator_traits<T>::value_type*, int)
{
    return {};
}

template <class T>
constexpr std::false_type is_iterator(void*, long)
{
    return {};
}

template <class T>
constexpr auto is_iterator_v = is_iterator<T>(nullptr, 0);

Also, an alternative implementation of print using tag-dispatching, which you may or may not find more readable:

namespace details
{
    template <class T>
    void print(const T &value, std::false_type)
    {
        std::cout << value;
    }

    template <class T>
    void print(const T &value, std::true_type)
    {
        std::cout << "(IteratorTo ";
        print(*value, is_iterator_v<T>);
        std::cout << ')';
    }
}

template <class T>
void print(const T &value)
{
    details::print(value, details::is_iterator_v<T>);
}
Comments