I though about SFINAE and using type traits, but was totally unable to come up with code. I never wrote any TMP code, in fact, I just read some books and articles about it. So the interviewer came up with his solution, which was to embed to type argument into a function parameter template, and create a "default function" for built-ins. That would look like that:
template <typename T> void print_r(const T& t) { std::cout << t; } template <typename T> void print_r(const std::vector<T>& v) { typedef typename std::vector<T>::const_iterator iter_t; for(iter_t iter = v.begin(); iter != v.end(); iter++) print_r(*iter); }
Yes, of course it works. But the problem here is that it only works for
std::vector
, and you would have to write such a function for every collection type in the template library. Not sexy. And C++ does not have a generic collection hierarchy like C# has (which would be stupid anyway.) Since I won't give up so easily, I wrote my code using SFINAE. These functions detects the presence of const_iterator
s and recurse into the collection if such a type is present : template <typename T> struct has_const_iterator { typedef char yes[1]; typedef char no[2]; template <typename C> static yes& test(typename C::const_iterator*); template <typename> static no& test(...); static const bool value = sizeof(test<T>(0)) == sizeof(yes); }; template <typename T> void print_r(const T& t, char(*)[!(has_const_iterator::value)] = 0) { std::cout << t; } template <typename T> void print_r(const T& v, char(*)[has_const_iterator<T>::value] = 0) { std::cout << "["; typedef typename T::const_iterator iter_t; for(iter_t iter = v.begin(); iter != v.end(); iter++) { if(iter != v.begin()) std::cout << " "; print_r(*iter); } std::cout << "]"; }
Okay, this is longer, but much more flexible. Now we can use these function templates with pretty much any container type which has a const_iterator, even proxy or hand-made classes.
The only problem is that the
char(*)[has_const_iterator::value] = 0
is quite bulky. This can be improved (but not shortened) by using enable_if
from boost or the new(future) standard library.You may want to follow me on twitter.
No comments:
Post a Comment