How to limit an iterator as a forward iterator?

I have a function that should list an iterator several times, but according to MSDN : "Once you enlarge any copy of the input iterator, none of the other copies can be boldly compared, dereferenced, or enlarged after that."

So, in order to simplify the task, instead of creating a separate implementation for non-forward-iterators that copies the data and lists the copy, I want to limit my method to only use forwarding iterators and statically reject input iterators.

Now I have something like:

template<typename It, typename TCallback /*signature: bool(value_type)*/> bool EnumerateTwice(const It &begin, const It &end, TCallback callback) { for (It it = begin; it != end; ++it) if (!callback(*it)) return false; for (It it = begin; it != end; ++it) if (!callback(*it)) return false; return true; } 

but nothing limits It as a forward iterator.

How to place this restriction on a template function? (C ++ 03)

+6
source share
4 answers

You can use SFINAE and replace bool with:

 typename enable_if< is_same<typename std::iterator_traits<It>::iterator_category, std::forward_iterator_tag>::value, bool>::type 

You may need to define is_same and enable_if yourself if you do not want to pull them from Boost or TR1:

 template <typename A, typename B> struct is_same { static const bool value = false; }; template <typename T> struct is_same<T, T> { static const bool value = true; }; template <bool, typename> struct enable_if { }; template <typename T> struct enable_if<true, T> { typedef T type; }; 
+5
source

Not tested, but you could try something line by line:

 template<typename It, typename TCallback /*signature: bool(value_type)*/> bool EnumerateTwice_Interal(const It &begin, const It &end, TCallback callback, std::forward_iterator_tag) { //do your things } template<typename It, typename TCallback /*signature: bool(value_type)*/> bool EnumerateTwice(const It &begin, const It &end, TCallback callback) { EnumerateTwice_Internal(begin, end, callback, typename std::iterator_traits<It>::iterator_category()); } 
+4
source

You can do this using std::enable_if :

 #include <iterator> #include <type_traits> #include <utility> template <typename It, typename TCallback> typename std::enable_if<std::is_base_of<std::forward_iterator_tag, typename std::iterator_traits<It>::iterator_category>::value, bool>::type EnumerateTwice(It begin, It end, TCallback) { ... } 

This uses a class from C ++ 11, but all this can be done in C ++ 03 as well.

+3
source

To extend the answer to rodrigo - I found this solution and thought it worth mentioning:

 struct True { unsigned char _[2]; operator bool() const { return true; } }; char is_forward_iterator(std::input_iterator_tag const *) { return 0; } True is_forward_iterator(std::forward_iterator_tag const *) { return True(); } 

Now, if you want to test it inside some function, you can say:

 if (is_forward_iterator(static_cast<iterator_traits<It>::iterator_category*>(0))) { ... } 

and if you want to check it in templates, you can check:

 sizeof( is_forward_iterator(static_cast<iterator_traits<It>::iterator_category*>(0)) ) > 1 

with the main advantage of this method is that it avoids the declaration of templates (for example, for better compilation speed).

0
source

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


All Articles