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 want to create ArrayList like this:
ArrayList arr(1, 5);
The compiler doesn't know what version of the function should pick.
How can I solve this?
The constructors:
ArrayList(const size_type elem_amount, value_type elem) : arr_size { elem_amount }, arr_capacity{ elem_amount }
{
array = std::uninitialized_fill_n(allocator.allocate(arr_size), arr_size, elem) - arr_size;
first = array;
last = array + arr_size - 1;
}
template<typename ITER>
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;
}
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;
}
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 Iterator or InputIterator:
template<typename ITER>
concept Iterator = requires {
typename std::iterator_traits<ITER>::iterator_category;
};
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).