You might try Substitution Failure Is Not An Error (SFINAE) techniques here.
Firstly, you need a function to determine whether a type has an iterator member...
template <typename T>
struct Has_Iterator
{
template <typename>
static char test(...);
template <typename U>
static int test(typename U::const_iterator*);
static const bool result = sizeof test<T>(0) != sizeof(char);
};
In the above code, the C++ Standard requires test(typename U::const_iterator*)
to be used in preference to the vague "..." parameter match, so long as U
is actually a struct/class with a const_iterator
member type. Otherwise, you have a "substitution failure" - which is not a fatal error stopping compilation (hence SFINAE), and the attempt to find a matching function is satified by test(...)
. As the return types of the two functions differ, the sizeof
operator can test which one has been matched, setting the result
boolean appropriately.
Then you can forward requests to print things to template specialisations that support them...
template <typename T>
void print(const T& data)
{
printer<Has_Iterator<T>::result, T>()(data);
}
// general case handles types having iterators...
template <bool Has_It, typename T>
struct printer
{
void operator()(const T& data)
{
for (typename T::const_iterator i = data.begin(); i != data.end(); ++i)
std::cout << *i << ' ';
std::cout << '\n';
}
};
// specialisation for types lacking iterators...
template <typename T>
struct printer<false, T>
{
void operator()(const T& data)
{
std::cout << data << '\n';
}
};