【问题标题】:Vectors do not satisfy std::ranges::contiguous_range in Eigen 3.4向量在 Eigen 3.4 中不满足 std::ranges::contiguous_range
【发布时间】:2022-01-23 06:28:37
【问题描述】:

为什么Eigen::VectorXd 不满足std::ranges::contiguous_range 的概念?即static_assert(std::ranges::contiguous_range<Eigen::VectorXd>); 不编译。

另外,是否有可能专门化模板以使 Eigen 向量满足连续范围概念?例如,我们可以专门化std::ranges::enable_borrowed_range 以使任何范围都满足std::range::borrowed_range 的概念。换句话说,有没有办法让上面的静态断言编译?

【问题讨论】:

    标签: c++ eigen c++20 eigen3 std-ranges


    【解决方案1】:

    必须选择连续范围。无法仅通过查看迭代器来确定它是连续访问还是只是随机访问。考虑一下deque<int>::iteratorvector<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;
    };
    

    【讨论】:

    • 非常感谢您的精彩回答!我同意如果Eigen 解决了这个问题会更好,但是为什么你特别建议不要专门化std::iterator_traits?如果我知道 Eigen 向量确实对连续范围概念进行建模,那么“通知”迭代器特征关于这一事实有什么问题?
    • @fdev 必须尽早完成,并且程序的每个部分都必须对此达成一致。由于程序的不同部分对iterator_traits&lt;I&gt; 的含义有不同的理解,因此很容易导致违反 odr。
    • 我明白了!所以唯一的问题是如果我忘记包含我专门研究的头文件std::iterator_traits,但只要不发生这种情况我应该是安全的。就我而言,我会在存储库中所有其他文件严格要求的头文件中执行此操作(它定义了最基本的数据类型)。因此,我绝不应该违反 ODR!
    • 不幸的是,如果我想满足所有 Eigen 向量类型(即动态和固定大小向量)的连续范围概念,则专门化 std::iterator_traits 效果不佳、段和Eigen::Ref 对象。您知道是否有一种方法可以在不列出所有可能类型的情况下实现这一目标?
    • @fdev 将此作为功能请求如何:gitlab.com/libeigen/eigen/-/issues
    猜你喜欢
    • 2020-01-27
    • 1970-01-01
    • 2020-01-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多