In principle, this is not the right approach in which general algorithms should be implemented . iterator s and std::iterator_traits usually used to determine the base type and allowed operations. If you want to execute a different algorithm (with a different complexity) based on which interface the container provides (random access, non-random access), you should do what follows.
First of all, your general algorithm should work on ranges, not containers. That is, it should look like any of the <algorithm> functions:
template <typename Iterator> void sorty(Iterator first, Iterator last);
Secondly, you must write helper functions that use a different sorting method to make the most of the container interface in order to work in the most efficient way:
// O(N*lgN) complexity sorting template <typename Iterator> void sorty_helper(Iterator first, Iterator last, std::random_access_iterator_tag); // O(N*N) complexity sorting template <typename Iterator> void sorty_helper(Iterator first, Iterator last, std::forward_iterator_tag);
Now your original sorty function should actually redirect the iterators only to the corresponding helper function, based on the type of iterator obtained through std::iterator_traits :
template <typename Iterator> void sorty(Iterator first, Iterator last) { sorty_helper(first, last, typename std::iterator_traits<Iterator>::iterator_category()); }
Demo 1
An alternative approach would be to enable / disable function templates using the SFINAE technique:
#include <iterator> #include <type_traits> template <typename Iterator> typename std::enable_if< std::is_same<typename std::iterator_traits<Iterator>::iterator_category, std::random_access_iterator_tag>::value >::type sorty(Iterator first, Iterator last) { // O(N*lgN) complexity sorting } template <typename T> struct AlwaysFalse : std::false_type {}; template <typename Iterator> typename std::enable_if< !std::is_same<typename std::iterator_traits<Iterator>::iterator_category, std::random_access_iterator_tag>::value >::type sorty(Iterator first, Iterator last) { // other sorting algorithm or print out a user-friendly error static_assert(AlwaysFalse<Iterator>{}, "Iterator must be a random-access iterator!"); }
Demo 2
Where enable_if and is_same are types of type C ++ 11, then in C ++ 03 you can define the following:
template <bool b, typename T = void> struct enable_if {}; template <typename T> struct enable_if<true, T> { typedef T type; }; template <typename T, typename U> struct is_same { static const bool value = false; }; template <typename T, typename U> const bool is_same<T, U>::value; template <typename T> struct is_same<T, T> { static const bool value = true; }; template <typename T> const bool is_same<T, T>::value;
If, on the other hand, you want to check only the existence of the at member function and make decisions based on compilation based on this, you can use the SFINAE expression technique:
template <typename Container> auto sorty_helper(Container&& container, int) -> decltype(void(std::forward<Container>(container).at(0))) { // O(N*lgN) complexity sorting } template <typename Container> void sorty_helper(Container&& container, void*) { // O(N*N) complexity sorting } template <typename Container> void sorty(Container&& container) { sorty_helper(std::forward<Container>(container), 0); }
Demo 3
In C ++ 03, to verify the existence of a member function of a given signature, a handwritten attribute is required:
template <typename T> struct has_at { typedef char (&yes)[1]; typedef char (&no)[2]; template <typename U, U u> struct SFINAE {}; template <typename U> static yes test(SFINAE<typename U::reference(U::*)(std::size_t), &U::at>*); template <typename U> static no test(...); static const bool value = sizeof(test<T>(0)) == sizeof(yes); };
which can be used in combination with enable_if :
template <bool b, typename T = void> struct enable_if {}; template <typename T> struct enable_if<true, T> { typedef T type; }; template <typename Container> typename enable_if<has_at<Container>::value>::type sorty(Container& container) {
Demo 4