A function template that takes nothing more than a bidirectional iterator or pointer

I need a function template that takes two iterators, which can be pointers. If two arguments are random_access iterators, I want the return type to be an object

std::iterator<random_access_iterator_tag, ...> type

else a

std::iterator<bidirectional_iterator_tag, ...> .

I also want the code to refuse compilation if the arguments are neither a bidirectional iterator nor a pointer. I cannot have dependencies on third-party libraries, for example. Boost

Could you help me with the signature of this function so that it accepts bidirectional iterators, as well as pointers, but does not say input_iterator, output_iterator, forward_iterators.

One partial solution I can think of is as follows

 template<class T> T foo( T iter1, T iter2) { const T tmp1 = reverse_iterator<T>(iter1); const T tmp2 = reverse_iterator<T>(iter2); // do something } 

The idea is that if it is not bidirectional, the compiler will not allow me to build a reverse_terator from it.

+4
source share
2 answers

slightly simpler than the previous answer, no dependency on std :: enable_if:

 namespace detail { template<class T> T do_foo(T iter1, T iter2, std::random_access_iterator_tag t) { cout << "do_foo random_access" << endl; return iter1; } template<class T> T do_foo(T iter1, T iter2, std::bidirectional_iterator_tag t) { cout << "do_foo bidirectional" << endl; return iter1; } } template<class T> void foo(T iter1, T iter2) { typename std::iterator_traits<T>::iterator_category t; detail::do_foo(iter1, iter2, t); } int main (int argc, const char * argv[]) { std::vector<int> v; foo(v.begin(), v.end()); std::list<int> l; foo(l.begin(), l.end()); return 0; } 

The solution also supports other iterator_categories derived from std :: random_access_iterator_tag or std :: bidirectional_iterator_tag (if any), while std :: same <> checks for strict categorical compliance.

+2
source

Here is an example with enable_if based on iterator tags. Substitution is not performed if the specified T does not have a t iterator_category typedef and therefore overload is not taken into account when resolving overload.

Since you cannot use C ++ 11, see the enable_if and is_same man pages to see how you can implement it yourself.

 #include <iterator> #include <type_traits> #include <iostream> #include <vector> #include <list> template<typename T> typename std::enable_if< std::is_same< typename T::iterator_category, std::bidirectional_iterator_tag >::value, T >::type foo(T it) { std::cout << "bidirectional\n"; return it; } template<typename T> typename std::enable_if< std::is_same< typename T::iterator_category, std::random_access_iterator_tag >::value, T >::type foo(T it) { std::cout << "random access\n"; return it; } // specialization for pointers template<typename T> T* foo(T* it) { std::cout << "pointer\n"; return it; } int main() { std::list<int>::iterator it1; std::vector<int>::iterator it2; int* it3; std::istream_iterator<int> it4; foo(it1); foo(it2); foo(it3); //foo(it4); // this one doesn't compile, it4 is an input iterator } 

Living example .

According to @JonathanWakely's comment, we can get rid of specialization for pointers if we use std :: iterator_traits . Then the part of typename T::iterator_category becomes

 typename std::iterator_traits<T>::iterator_category 
+3
source

Source: https://habr.com/ru/post/1445599/


All Articles