必须选择连续范围。无法仅通过查看迭代器来确定它是连续访问还是只是随机访问。考虑一下deque<int>::iterator 和vector<int>::iterator 之间的区别——它们提供所有相同的操作并返回所有相同的东西,除非vector<int>::iterator 明确告诉你,否则你怎么知道?
Eigen 的迭代器还没有这样做。事实上,在 C++20 之前,并没有连续迭代器的概念。这是 C++20 范围的新功能。
如果您尝试验证它是连续的,您会看到这一点:
using I = Eigen::VectorXd::iterator;
static_assert(std::contiguous_iterator<I>);
在 gcc 上,诊断表明:
/opt/compiler-explorer/gcc-trunk-20211221/include/c++/12.0.0/concepts:67:13: required for the satisfaction of 'derived_from<typename std::__detail::__iter_concept_impl<_Iter>::type, std::contiguous_iterator_tag>' [with _Iter = Eigen::internal::pointer_based_stl_iterator<Eigen::Matrix<double, -1, 1, 0, -1, 1> >]
/opt/compiler-explorer/gcc-trunk-20211221/include/c++/12.0.0/concepts:67:28: note: 'std::contiguous_iterator_tag' is not a base of 'std::random_access_iterator_tag'
67 | concept derived_from = __is_base_of(_Base, _Derived)
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
基本上,我们的类别是随机访问的,而不是连续的。
Eigen 正确执行此操作的方法是添加:
template<typename XprType>
class pointer_based_stl_iterator
{
enum { is_lvalue = internal::is_lvalue<XprType>::value };
typedef pointer_based_stl_iterator<typename internal::remove_const<XprType>::type> non_const_iterator;
typedef pointer_based_stl_iterator<typename internal::add_const<XprType>::type> const_iterator;
typedef typename internal::conditional<internal::is_const<XprType>::value,non_const_iterator,const_iterator>::type other_iterator;
// NOTE: in C++03 we cannot declare friend classes through typedefs because we need to write friend class:
friend class pointer_based_stl_iterator<typename internal::add_const<XprType>::type>;
friend class pointer_based_stl_iterator<typename internal::remove_const<XprType>::type>;
public:
typedef Index difference_type;
typedef typename XprType::Scalar value_type;
typedef std::random_access_iterator_tag iterator_category;
+ typedef std::contiguous_iterator_tag iterator_concept;
typedef typename internal::conditional<bool(is_lvalue), value_type*, const value_type*>::type pointer;
typedef typename internal::conditional<bool(is_lvalue), value_type&, const value_type&>::type reference;
};
这将使其成为 C++20 连续迭代器。
或者,您可以在外部自己执行此操作,尽管这不是一个好主意(实际上,应该正确执行此操作的是 Eigen),并且必须在使用任何范围之前完成:
template <>
struct std::iterator_traits<I> {
using iterator_concept = std::contiguous_iterator_tag;
using iterator_category = std::random_access_iterator_tag;
using value_type = typename I::value_type;
using difference_type = typename I::difference_type;
};