将现有范围类型调整为与 c++20 范围兼容而不进行更改

问题描述 投票:0回答:1

我的目标是采用一些虚幻引擎类型并使它们可以与 c++20 范围一起使用。我从

TArray
开始,我认为这是最简单的。它本质上是一个
std::vector
,连续存储等等。所以总的来说,它可以很容易地用这个来表示:

template <typename T, typename A = int>
struct TArray {
    T* GetData() const;
    size_t Num() const;
};

我基本上为此制作了一个包装,看起来有点像:

        template <template <typename, typename> typename TCont, typename T, typename TAlloc>
        class rangified_array
        {
        public:
            using ue_container = TCont<T, TAlloc>;

            using value_type             = T;
            using allocator_type         = TAlloc;
            ...

            rangified_array(ue_container& container)
                : m_container{container}
            {
            }

    ...

我会将完整的代码放在问题的末尾。您还可以在这里玩玩具示例:https://godbolt.org/z/EEEzPWeG7

基本上我似乎有一个完全兼容的范围数组,它与 std::range 算法一起使用。它通过了以下检查:

using r_t = decltype(make_range(tarr));
static_assert(std::sentinel_for<r_t::iterator, r_t::iterator>);
static_assert(std::ranges::range<r_t>);

但是当我这样使用它时:

auto t = make_range(tarr) | v::transform([](auto i) { return i + 1;}); 

我遇到了一个很大的失败,我不符合范围适配器操作员的限制。 海湾合作委员会建议:

ranges:955:5: note: constraints not satisfied
ranges: In substitution of 'template<class _Self, class _Range>  requires (__is_range_adaptor_closure<_Self>) && (__adaptor_invocable<_Self, _Range>) constexpr auto std::ranges::views::__adaptor::operator|(_Range&&, _Self&&) [with _Self = std::ranges::views::__adaptor::_Partial<std::ranges::views::_Transform, foo()::<lambda(auto:10)> >; _Range = detail::rangified<TArray<int>, int, int>]':
<source>:201:77:   required from here
  201 |         auto t = make_range(tarr) | v::transform([](auto i) { return i + 1;});
      |                                                                             ^
ranges:914:13:   required for the satisfaction of '__adaptor_invocable<_Self, _Range>' [with _Self = std::ranges::views::__adaptor::_Partial<std::ranges::views::_Transform, foo::._anon_113>; _Range = detail::rangified<TArray<int, int>, int, int>]
ranges:915:9:   in requirements  [with _Adaptor = std::ranges::views::__adaptor::_Partial<std::ranges::views::_Transform, foo::._anon_113>; _Args = {detail::rangified<TArray<int, int>, int, int>}]

我找不到有关这些限制的任何文档。有谁知道我做错了什么?

完整代码传入

#include <ranges>

template <typename T, typename A = int>
struct TArray {

    T* GetData() const;
    size_t Num() const;

};

    
    namespace detail
    {
        // Currently only the TArray is supported. If you need to range adapt something else you can add the
        // specialisation below.

        template <typename TCont, typename T, typename TAlloc>
        class rangified;

       struct sentinal
       {
       };

        template <bool is_const, typename T>
        class TArrayIterator
        {
        public:
            TArrayIterator() = default;

            explicit TArrayIterator(const T* p, const T* end)
                : m_p{const_cast<T*>(p)}, m_end{end} {}

            explicit TArrayIterator(T* p, const T* end)
                requires (!is_const)
                : m_p{p}, m_end{end} {}

            using value_type        = T;
            using element_type      = T;
            using iterator_category = std::contiguous_iterator_tag;

            T*              operator->() const { return m_p; }
            T&              operator*() const { return *m_p; }
            T&              operator[](size_t p) const { return *(m_p + p); }
            TArrayIterator& operator++()
            {
                ++m_p;
                return *this;
            }

            TArrayIterator operator++(int)
            {
                TArrayIterator out = *this;
                ++(*this);
                return out;
            }

            TArrayIterator& operator--()
            {
                --m_p;
                return *this;
            }

            TArrayIterator operator--(int)
            {
                TArrayIterator out = *this;
                --(*this);
                return out;
            }

            TArrayIterator& operator+=(size_t i)
            {
                m_p += i;
                return *this;
            }

            TArrayIterator& operator-=(size_t i)
            {
                m_p -= i;
                return *this;
            }


friend bool operator==(TArrayIterator lhs, sentinal) {
    return lhs.m_p == lhs.m_end;
}

friend bool operator==(sentinal, TArrayIterator rhs ) {
    return rhs.m_p == rhs.m_end;
}
            friend auto operator<=>(TArrayIterator, TArrayIterator) = default;
            friend bool operator==(TArrayIterator, TArrayIterator) = default;
            friend std::ptrdiff_t operator-(TArrayIterator lhs, TArrayIterator rhs) { return lhs.m_p - rhs.m_p; }
            friend TArrayIterator operator+(TArrayIterator lhs, int rhs) { return TArrayIterator{lhs.m_p + rhs}; }
            friend TArrayIterator operator-(TArrayIterator lhs, int rhs) { return TArrayIterator{lhs.m_p - rhs}; }
            friend TArrayIterator operator+(int lhs, TArrayIterator rhs) { return TArrayIterator{lhs + rhs.rhs}; }

        private:
            T* m_p = nullptr;
            const T* m_end = nullptr;
        };

        template <template <typename, typename> typename TCont, typename T, typename TAlloc>
        class rangified_array
        {
        public:
            using ue_container = TCont<T, TAlloc>;

            using value_type             = T;
            using allocator_type         = TAlloc;
            using size_type              = std::size_t;
            using difference_type        = std::ptrdiff_t;
            using reference              = T&;
            using const_reference        = const T&;
            using pointer                = T*;
            using const_pointer          = const T*;
            using iterator               = TArrayIterator<false, T>;
            using const_iterator         = TArrayIterator<true, T>;
            using reverse_iterator       = std::reverse_iterator<iterator>;
            using const_reverse_iterator = std::reverse_iterator<const_iterator>;

            rangified_array(ue_container& container)
                : m_container{container}
            {
            }

            const_iterator begin() const { return const_iterator{m_container.GetData(), m_container.GetData() +  + m_container.Num()}; }
            const_iterator end() const { 
                auto last = m_container.GetData() + m_container.Num();
                return const_iterator{last, last };
            }

            iterator begin() { return iterator{
                m_container.GetData(), m_container.GetData()  + m_container.Num()}; 
                }
            iterator end() { 
                auto last = m_container.GetData() + m_container.Num();
                return iterator{last, last };}

        private:


            ue_container& m_container;
        };

        // This provides a specialisation for the const and non const TArray overloads
        template <typename T, typename TAlloc>
        class rangified<TArray<T, TAlloc>, T, TAlloc> : public rangified_array<TArray, T, TAlloc>
        {
            using rangified_array<TArray, T, TAlloc>::rangified_array;
        };

        template <typename TUeComponent>
        struct ue_underlying_types;

        template <typename T, typename TAlloc>
        struct ue_underlying_types<TArray<T, TAlloc>>
        {
            using container_type = TArray<T, TAlloc>;
            using value_type     = T;
            using allocator_type = TAlloc;
        };

        template <typename T>
        struct is_range_adapter_supported : public std::false_type
        {
        };

        template <typename T, typename TAlloc>
        struct is_range_adapter_supported<TArray<T, TAlloc>> : public std::true_type
        {
        };

        template <typename T>
        constexpr inline bool is_range_adapter_supported_v = is_range_adapter_supported<T>::value;

    }

    template <typename TUeContainer>
        requires detail::is_range_adapter_supported_v<std::remove_cv_t<std::remove_reference_t<TUeContainer>>>
    auto make_range(TUeContainer&& arr)
    {
        using T = detail::ue_underlying_types<std::remove_reference_t<TUeContainer>>;
        using R = detail::rangified<typename T::container_type, typename T::value_type, typename T::allocator_type>;
        return R{arr};
    }
c++ std-ranges
1个回答
0
投票

我遇到了一个很大的失败,我不符合范围适配器操作员的限制。

你是对的。 注意 [范围.refinements] p6:

viewable_range
概念指定了可以安全转换为视图的范围类型的要求。

这种转换发生在以下情况:你的范围被包裹在

std::views::all
中,这是由于
std::ranges::transform_view
的推导指南而发生的。 您的
rangifed_array
满足大多数要求,但失败了:

(!view<remove_cvref_t<T>> &&
is_lvalue_reference_v<T> || (movable<remove_reference_t<T>> && !is-initializer-list<T>))

您的

rangified_array
需要
movable
(这不是因为它不可分配,因为您存储了引用),或者您需要将事物作为左值传递:

auto t = v::transform(r, add_1);

总是必须做后者是相当愚蠢的,所以不要存储引用并使你的类更像一个跨度。 事实上,我真的不明白为什么你不直接添加从

TArray
std::span
的转换。 如果你非常想要自己的类型,你可以只存储一个指针而不是引用,并使你的类型可以这样分配。

© www.soinside.com 2019 - 2024. All rights reserved.