How to make constructor accept all type of iterators?

后端 未结 2 854
梦谈多话
梦谈多话 2020-12-12 03:50

I am creating a custom Vector/ArrayList class. But i´m having troubles creating the iterative version of the constructor. The following code works, but the problem is when i

相关标签:
2条回答
  • 2020-12-12 04:22

    With C++20 you can use concepts

    Note that some compilers start supporting the new features of C++20, including concepts: https://en.cppreference.com/w/cpp/compiler_support

    So to differentiate between your two constructors, with C++20 you restrict the allowed template arguments using a concept type:

    template<std::random_access_iterator ITER>
             // see comment by Nathan Oliver for using random_access_iterator 
    ArrayList(ITER begin, ITER end)
    : arr_size{ static_cast<size_type>(end - begin) }, arr_capacity{ arr_size }
    {
        std::uninitialized_copy(begin, end, array = allocator.allocate(arr_size));
        first = array;
        last = array + arr_size - 1;
    }
    

    If you wish to define your own concept (no need to in this case, just for the sake of the exercise), you can define a concept for an Iterator:

    template<typename ITER>
    concept Iterator = requires {
        typename std::iterator_traits<ITER>::iterator_category;
    };
    

    Or, for the use in this case, a RandomAccessIterator:

    template<typename ITER>
    concept RandomAccessIterator = std::is_base_of_v <
        std::random_access_iterator_tag,
        typename std::iterator_traits<ITER>::iterator_category
    >;
    

    And use it the same as above, for example:

    template<RandomAccessIterator ITER>
    ArrayList(ITER begin, ITER end)  // ...
    

    Code: https://godbolt.org/z/gBevvC

    (Note that RandomAccessIterator should be part of ranges, but can be implemented on your own as above).

    0 讨论(0)
  • 2020-12-12 04:26

    What you need to do is use SFINAE to constrain the template to only work when the template type is deduced to be an iterator type. Since you do arr_size{ static_cast<size_type>(end - begin) } to initialize size this means that you expect the iterators to be random access. We can check for that using the iterator_category of std::iterator_traits Doing that gives you

    template<typename ITER, 
             std::enable_if_t<std::is_base_of_v<typename std::iterator_traits<ITER>::iterator_category, 
                                                std::random_access_iterator_tag>, bool> = true>
    ArrayList(ITER begin, ITER end) : arr_size{ static_cast<size_type>(end - begin) }, arr_capacity{ arr_size }
    {
        std::uninitialized_copy(begin, end, array = allocator.allocate(arr_size));
        first = array;
        last = array + arr_size - 1;
    }
    
    0 讨论(0)
提交回复
热议问题