C++ 如何使构造函数接受所有类型的迭代器?
我正在创建一个自定义的Vector/ArrayList类。但是我在创建构造函数的迭代版本时遇到了麻烦。下面的代码可以工作,但问题是当我想像这样创建ArrayList时: arraylistarr1,5 编译器不知道函数应该选择哪个版本 我怎样才能解决这个问题 施工人员:C++ 如何使构造函数接受所有类型的迭代器?,c++,class,templates,iterator,C++,Class,Templates,Iterator,我正在创建一个自定义的Vector/ArrayList类。但是我在创建构造函数的迭代版本时遇到了麻烦。下面的代码可以工作,但问题是当我想像这样创建ArrayList时: arraylistarr1,5 编译器不知道函数应该选择哪个版本 我怎样才能解决这个问题 施工人员: ArrayList(const size_type elem_amount, value_type elem) : arr_size { elem_amount }, arr_capacity{ elem_amount } {
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;
}
您需要做的是将模板约束为仅当模板类型被推断为迭代器类型时才起作用。由于您需要arr_size{static_castend-begin}来初始化大小,这意味着您希望迭代器是随机访问的。我们可以使用迭代器(iterator_)来检查这一点
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;
}
您需要做的是将模板约束为仅当模板类型被推断为迭代器类型时才起作用。由于您需要arr_size{static_castend-begin}来初始化大小,这意味着您希望迭代器是随机访问的。我们可以使用迭代器(iterator_)来检查这一点
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;
}
使用C++20可以使用概念
请注意,一些编译器开始支持C++20的新功能,包括以下概念:
因此,为了区分两个构造函数,使用C++20可以使用概念类型限制允许的模板参数:
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;
}
如果您希望定义自己的概念,在本例中无需这样做,仅为了练习,您可以为迭代器定义一个概念:
template<typename ITER>
concept Iterator = requires {
typename std::iterator_traits<ITER>::iterator_category;
};
或者,在本例中使用RandomAccessIterator:
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
>;
并使用与上述相同的方法,例如:
template<RandomAccessIterator ITER>
ArrayList(ITER begin, ITER end) // ...
代码:
请注意,这应该是范围的一部分,但可以如上所述自行实现。使用C++20可以使用概念
请注意,一些编译器开始支持C++20的新功能,包括以下概念:
因此,为了区分两个构造函数,使用C++20可以使用概念类型限制允许的模板参数:
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;
}
如果您希望定义自己的概念,在本例中无需这样做,仅为了练习,您可以为迭代器定义一个概念:
template<typename ITER>
concept Iterator = requires {
typename std::iterator_traits<ITER>::iterator_category;
};
或者,在本例中使用RandomAccessIterator:
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
>;
并使用与上述相同的方法,例如:
template<RandomAccessIterator ITER>
ArrayList(ITER begin, ITER end) // ...
代码:
请注意,这应该是范围的一部分,但可以如上所述自行实现。我将使用std::is_base_of_v,参数顺序颠倒,而不是is_same_v。这使得更改为不太特定的类型变得更容易,并且将来会根据C++20 std::continuous_iterator_标记对代码进行验证。@aschepler很好的调用。我只是在想这个。我将使用std::is_base_of_v,参数顺序颠倒,而不是is_same_v。这使得更改为不太特定的类型变得更容易,并且将来会根据C++20 std::continuous_iterator_标记对代码进行验证。@aschepler很好的调用。我只是在想这个。请注意,输入迭代器对于契约来说太弱了。执行arr_size{static_castend-begin}至少需要随机访问迭代器。@NathanOliver-确实如此。修正了。请注意,输入迭代器对于契约来说太弱了。执行arr_size{static_castend-begin}至少需要随机访问迭代器。@NathanOliver-确实如此。固定的