我的目标是采用一些虚幻引擎类型并使它们可以与 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};
}
我遇到了一个很大的失败,我不符合范围适配器操作员的限制。
你是对的。 注意 [范围.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
的转换。
如果你非常想要自己的类型,你可以只存储一个指针而不是引用,并使你的类型可以这样分配。