提问人:Matt 提问时间:7/18/2022 更新时间:7/22/2022 访问量:186
如何在构造函数中创建一个可以接受满足 std::forward_iterator<T> 的任何类型的模板类?
How do I create a template class that can accept any type that satisfies std::forward_iterator<T> in the constructor?
问:
我正在制作一个循环迭代器类,它的行为类似于前向迭代器,只是它在到达范围的末尾后循环回到起点。
template <typename T>
struct CircularIterator
{
CircularIterator(T* begin, T* end);
// Omitted overloaded operators
T* mBegin; // Points to beginning of range
T* mIter; // Current item
T* mEnd; // Points to end of range
};
没有从 STL 迭代器(例如)到原始指针 () 的转换。以下代码编译时出现错误:std::vector<int>::iterator>
T*
std::vector<int> vec{1, 2, 3};
CircularIterator<int> iter(vec.begin(), vec.end());
error: cannot convert ‘__gnu_cxx::__normal_iterator<int*, std::vector<int> >’ to ‘int*’ in initialization
如何在构造函数中创建一个可以接受满足 std::forward_iterator<T>
的任何类型的模板类?我想避免为所使用的每个迭代器类型创建新的模板实例(例如,new for 和 .)CircularIterator
std::array<T>::iterator
std::deque<T>::iterator
任何建议都将被采纳。我绝对处于我的模板/概念知识的尽头,并期待任何资源来了解更多信息。谢谢。
答:
您可以约束必须满足 std::forward_iterator
的模板参数,例如I
CircularIterator
#include <iterator>
template <std::forward_iterator I>
struct CircularIterator
{
CircularIterator() = default;
CircularIterator(I begin, I end);
// Omitted overloaded operators
I mBegin; // Points to beginning of range
I mIter; // Current item
I mEnd; // Points to end of range
};
请注意,您需要提供默认构造函数,因为需要默认构造函数。CircularIterator
std::forward_iterator
评论
前向迭代器不必是连续范围的迭代器,因此不合适。您可以改为存储迭代器本身。以下是将 SFINAE 与 和 一起使用的一种可能方法:T* mBegin
is_base_of_v
forward_iterator_tag
#include <iterator>
#include <type_traits>
template <class It, class EndIt,
std::enable_if_t<std::is_base_of_v<
std::forward_iterator_tag,
typename std::iterator_traits<It>::iterator_category>,int> = 0>
struct CircularIterator {
CircularIterator(It begin, EndIt end)
: mBegin(begin), mIter(begin), mEnd(end) {}
It mBegin; // Points to beginning of range
It mIter; // Current item
EndIt mEnd; // Points to end of range
};
如果你真的想存储以获得更少的类模板实例化,你必须要求迭代器改为:T*
contiguous_iterator
#include <concepts>
#include <iterator>
#include <type_traits>
template <class T>
struct CircularIterator {
CircularIterator(std::contiguous_iterator auto begin,
std::contiguous_iterator auto end)
: mBegin(begin != end ? &*begin : nullptr),
mIter(mBegin),
mEnd(std::next(mBegin, std::distance(begin, end))) {}
T* mBegin; // Points to beginning of range
T* mIter; // Current item
T* mEnd; // Points to end of range
};
// deduction guide:
template<class It, class EndIt>
CircularIterator(It, EndIt) ->
CircularIterator<typename std::iterator_traits<It>::value_type>;
...并像您的示例中一样使用:
#include <vector>
int main() {
std::vector<int> vec{1, 2, 3};
// specifying `<int>` is optional with the added deduction guide:
CircularIterator iter(vec.begin(), vec.end());
}
评论
vector<int>
vector<double>
vector<double>::iterator
vector<T>
&*end
是 UB,就像空容器一样&*begin
end
你的欲望是直接竞争的
在构造函数中满足 std::forward_iterator 的任何类型?
避免为所使用的每个迭代器类型创建新的模板实例
选择其中之一。和 之间没有共同的类型作为 的数据成员。std::vector<int>::iterator
std::deque<int>::iterator
CircularIterator<int>
在内存受限的环境中,你为什么要混合,而且首先?这些是更大的模板。如果您使用的唯一容器是 ,那么您持有 s 还是 in 有什么关系?std::array
std::vector
std::deque
std::vector
std::vector<int>::iterator
int *
CircularIterator<int>
评论
std::vector
std::deque
评论
template <typename Range> explicit CircularIterator(Range& range);
mBegin
mIter
mEnd
data()
std
data()
std::vector
std::array
std::basic_string
std::string_view
CircularIterator