How to check if the type std :: vector :: iterator is at compile time?

I have a problem when I need to determine if a given type is an instance of a known nested type such as std::vector::iterator at compile time. I would like to create a trait like is_std_vector_iterator :

 #include <type_traits> #include <vector> template<typename T> struct is_std_vector_iterator : std::false_type {}; template<typename T, typename Allocator> struct is_std_vector_iterator<typename std::vector<T,Allocator>::iterator> : std::true_type {}; int main() { return 0; } 

But I get a compiler error:

 $ g++ -std=c++0x test.cpp test.cpp:7: error: template parameters not used in partial specialization: test.cpp:7: error: 'T' test.cpp:7: error: 'Allocator' 

Is it possible to check the dependent type, for example std::vector<T,Allocator>::iterator ?


Here's a motivating example of using such a trait:

 template<typename Iterator> Iterator my_copy(Iterator first, Iterator last, Iterator result, std::true_type) { // iterators are just pointer wrappers; call memcpy memcpy(&*result, &*first, sizeof(typename Iterator::value_type) * last - first); return result + last - first; } template<typename Iterator> Iterator my_copy(Iterator first, Iterator last, Iterator result, std::false_type) { // use a general copy return std::copy(first, last, result); } template<typename Iterator> Iterator my_copy(Iterator first, Iterator last, Iterator result) { // dispatch based on the type of Iterator return my_copy(first, last, result, typename is_std_vector_iterator<Iterator1>::type()) } 
+6
source share
3 answers

Well, in the simplest case, it might look something like this:

 #include <type_traits> #include <vector> #include <list> #include <cstdio> template <typename T> typename std::enable_if< std::is_same<typename std::vector<typename T::value_type>::iterator, T>::value , void>::type do_something (T begin, T end) { std::printf ("Got vector iterators!\n"); } template <typename T> typename std::enable_if< !std::is_same<typename std::vector<typename T::value_type>::iterator, T>::value , void>::type do_something (T begin, T end) { std::printf ("Got something other than vector iterators!\n"); } template <typename T> typename std::enable_if<std::is_pod<T>::value, void>::type do_something (T begin, T end) { std::printf ("Got some POD iterators!\n"); } int main() { std::vector<int> ivec; std::list<int> ilist; char cdata[64]; do_something (ivec.begin (), ivec.end ()); do_something (ilist.begin (), ilist.end ()); do_something (&cdata[0], cdata + 32); return 0; } 

But the real problem arises when someone decides to use a dispenser other than the standard one. Since you want to check the iterator for some well-known type, and not for a well-known pattern, then you can basically use this and possibly extend it with some allocators that you know about. Otherwise, the template created using different types is a different type, and I'm not sure if there is a way to check if the type is an instance of the template specialized with any arbitrary parameter, probably there is no such way.

On the other hand, you can solve this problem differently. For example, what's the difference, is this a std::vector<...> iterator or not? It might make sense to check if it is random access or not, etc.

UPDATE:

For constantly laid out memory, I would say that it is best to use iterative features and check the random access tag. For instance:

 #include <type_traits> #include <functional> #include <vector> #include <list> #include <cstdio> template <typename T> struct is_random_access_iterator : std::is_same< typename std::iterator_traits<T>::iterator_category , std::random_access_iterator_tag> {}; template <typename T> typename std::enable_if<is_random_access_iterator<T>::value>::type do_something (T begin, T end) { std::printf ("Random access granted!\n"); } template <typename T> typename std::enable_if<!is_random_access_iterator<T>::value>::type do_something (T begin, T end) { std::printf ("No random access for us today!\n"); } int main() { std::vector<int> ivec; std::list<int> ilist; char cdata[32]; do_something (ivec.begin (), ivec.end ()); do_something (ilist.begin (), ilist.end ()); do_something (&cdata[0], cdata + sizeof (cdata) / sizeof (cdata[0])); return 0; } 

This will definitely be simpler and even more reliable than checking with std::vector with dispensers. However, even in this case, someone can fool you, if they really want to, buy by providing you with a random access iterator that provides seamless access to various pieces of memory, and you will have big problems as soon as you convert it into pointer arithmetic, not an iterator of overloaded operators. You can protect yourself from this only by comparing memory addresses when changing both the source pointer and the iterator, but there is no juice.

Hope this helps.

+3
source

You should look at the is_container_helper typetrait from a pretty printer . In a more sophisticated public version of this library, I call typetrait has_const_iterator (for example, here ):

 template<typename T> struct has_const_iterator { private: typedef char yes; typedef struct { char array[2]; } no; template<typename C> static yes test(typename C::const_iterator*); template<typename C> static no test(...); public: static const bool value = sizeof(test<T>(0)) == sizeof(yes); typedef T type; }; 
+1
source

AFAIK, you can get the iterator value type from iterator_traits<Iter>::value_type . Then you can check that std::vector<that_value_type, Alloc>::iterator really does (for example, with boost::is_same )

By the way, from your motivating example, I see that you will probably have problems with Alloc - if you do not plan to use custom allocators, you just leave it by default. There is no general solution that will work on all Alloc s.

+1
source

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


All Articles