How to implement a constructor so that it accepts only input iterators using typeid?

I want to implement a range constructor for a specific object, but I want to limit it to only accepting two input iterators.

I tried to compile this code with gcc 7.1.0.

File test.cpp

 #include <vector> #include <type_traits> #include <typeinfo> template <typename Iterator> using traits = typename std::iterator_traits<Iterator>::iterator_category; template <typename T> class A{ private: std::vector<T> v; public: template <typename InputIterator, typename = std::enable_if_t< typeid(traits<InputIterator>) == typeid(std::input_iterator_tag)> > A(InputIterator first, InputIterator last) : v(first, last) {} }; int main(){ std::vector<double> v = {1, 2, 3, 4, 5}; A<double> a(v.begin(), v.end()); } 

I get this compilation error with g++ test.cpp -o test :

  test.cpp: In function 'int main()': test.cpp:27:34: error: no matching function for call to 'A<double>::A(std::vector<double>::iterator, std::vector<double>::iterator)' A<double> a(v.begin(), v.end()); ^ test.cpp:22:7: note: candidate: template<class InputIterator, class> A<T>::A(InputIterator, InputIterator) A(InputIterator first, InputIterator last) : v(first, last) {} ^ test.cpp:22:7: note: template argument deduction/substitution failed: test.cpp: In substitution of 'template<bool _Cond, class _Tp> using enable_if_t = typename std::enable_if::type [with bool _Cond = ((const std::type_info*)(& _ZTISt26random_access_iterator_tag))->std::type_info::operator==(_ZTISt18input_iterator_tag); _Tp = void]': test.cpp:18:16: required from here test.cpp:19:49: error: call to non-constexpr function 'bool std::type_info::operator==(const std::type_info&) const' typeid(traits<InputIterator>) == test.cpp:18:16: note: in template argument for type 'bool' typename = std::enable_if_t< ^~~~~~~~ test.cpp:10:7: note: candidate: A<double>::A(const A<double>&) class A{ ^ test.cpp:10:7: note: candidate expects 1 argument, 2 provided test.cpp:10:7: note: candidate: A<double>::A(A<double>&&) test.cpp:10:7: note: candidate expects 1 argument, 2 provided 

I decided to use the default template parameter, because it is more suitable for designers. Using the typeid() operator is that I find it very easy to read when executing the code, but I can't get it to work in any way.

Other solutions look very strange and really obscure (for example, so that the InputIterator parameter has certain methods, such as * it or ++ it). If I cannot do this, I would appreciate a more or less easy-to-read solution.

+5
source share
2 answers

To make SFINAE, you need to make sure that the expression involvement is evaluated at compile time. For typeid , the following applies:

When applied to a polymorphic type expression, the evaluation of the typeid expression may include runtime overhead (virtual lookup table), otherwise the typeid expression will be allowed at compile time.

Thus, I would not consider typeid good option for static (compilation) polymorphism.

One way to solve your problem is to use label dispatch in combination with delegating business structures as follows:

 template <typename T> class A{ std::vector<T> v; template <typename InputIterator> A(InputIterator first, InputIterator last, std::input_iterator_tag) : v(first, last) {} public: template<typename InputIterator> A(InputIterator first, InputIterator last) : A(first, last, typename std::iterator_traits<InputIterator>::iterator_category()) {} }; 

Live demo

+3
source

Using the typeid() operator is that I find it very easy to read when executing the code, but I can't get it to work in any way.

typeid is a tool that is mainly used to request type information at runtime . Although you may find it more readable, this is not the right tool to work with, and it will confuse all other C ++ developers.


Other solutions look very strange and really obscure (for example, so that the InputIterator parameter has certain methods, such as *it or ++it )

I highly recommend you review this. InputIterator is a concept that describes the actual operations of a type. The whole idea of ​​the concept - performance testing - is nothing more than an InputIterator than its requirements .

The correct and idiomatic way to solve your problem is to create a variable / function constexpr bool is_input_iterator<T> that returns true if T meets the requirements of InputIterator . You can easily implement this using the discovery identifier ... and it does not become more readable than std::enable_if_t<is_input_iterator<T>> .

Here is a simplified example (I am not checking all the requirements):

 template <typename T> using supports_increment = decltype(++std::declval<T>()); template <typename T> using supports_dereference = decltype(*std::declval<T>()); template <typename T> constexpr bool is_input_iterator = std::experimental::is_detected_v<supports_increment, T> && std::experimental::is_detected_v<supports_dereference, T>; 

  template <typename InputIterator, typename = std::enable_if_t<is_input_iterator<InputIterator>>> A(InputIterator first, InputIterator last) : v(first, last) {} 

live example in wandbox

+1
source

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


All Articles